Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PolicyEngine in 30 seconds

A 30-second video that follows one real rule from statute to code to one family to all 50 states and DC. Every statistic on screen is a PolicyEngine output computed for this video, and every quotation is verbatim from a primary source; map dots are placed at random within each household's assigned congressional district.

The rendered files are attached to the latest release; bun run render rebuilds them into out/.

File Format
out/policyengine-30s-4k60.mp4 3840×2160, 60 fps, H.264 + AAC (master)
out/policyengine-30s-1080p60.mp4 1920×1080, 60 fps
out/policyengine-30s-vertical-1080x1920.mp4 1080×1920, 60 fps (Reels, Shorts, TikTok)
out/posters-16x9/, out/posters-9x16/ 2× stills at 12.9, 18.9, 23.6 and 28.8 s for thumbnails

A UK version (?country=uk) runs the same timeline on UK law: the Income Tax Act 2007 personal allowance, the policyengine-uk parameter file, a single parent in Manchester across earnings, and the 650 Westminster constituencies. Its national figures come from policyengine.py 6.2.0 (policyengine-uk 2.102.3) on the enhanced Family Resources Survey 2024-25; the survey microdata and the map's record-level draws stay out of the repo (data/uk/private/, git-ignored), so a fresh clone renders the UK map with placeholder dots under the MOCK DATA banner. Sources and checks for every UK string are in data/uk/README.md; tools/render_all.sh uk writes the files to out/uk/.

The story

Time Scene Source of what's on screen
0–4 s All of 26 U.S.C. § 24 (2,686 words) pours in under "…if the laws be so voluminous that they cannot be read…" (Federalist No. 62, 1788); the camera dives to "$2,200" in § 24(h)(2) data/statute.json — uscode.house.gov (current through 2026-09-18), cross-checked word for word against Cornell LII; Avalon Project for Federalist No. 62; 1788 from Founders Online
4–8.5 s "$2,200" flies into lines 7–26 of policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml; the § 24(h)(2) chip docks on the line that cites it; "What if Congress raised the credit to $3,000?" with the reform dict exactly as passed to policyengine.py data/base.yaml at the policyengine-us commit in data/base.yaml.commit; data/household.json → meta.reform_dict_passed
8.5–14 s "Take a married couple in Ohio," kids 4 and 8, one earner: the change in net income drawn across earnings from $0 to $120,000. "$0 below $42,200 in earnings. +$1,600 from $58,000." "The refundable part stays at $1,700 per child, so the extra $1,600 only offsets income tax the current credit leaves unpaid." Source tag: "policyengine.py 6.1.1 · static" data/earnings_sweep.json (policyengine_us axes, one point per $250), reproduced by policyengine.py's household calculator at all 601 points to $0.004 (data/compute/checks/curve_policyengine_py.json)
14–20 s Zoom out to all 50 states and DC: 12,000 weighted draws of households, each placed at random in its assigned congressional district; gainers light up grouped by gain (about $800, $1,600, then $2,400 and more); federal cost, share gaining, children out of poverty. Source tag: "policyengine.py 6.1.1 · populace_us_2024 · static / poverty: Supplemental Poverty Measure". The 12,000 draws cover 6,976 distinct households data/national.json, data/households_sample.json, district shapes from data/district_geography.json
20–25 s The same dots settle on the baseline of each income decile; bars show average change per household. Source tag: "policyengine.py 6.1.1 · populace_us_2024 · static" data/national.json → deciles.by_decile (full sample, not the 12,000 draws)
25–30 s The dots form the wordmark; tagline; "Free and open source · policyengine.org" —

Each chart carries its own small print in its bottom-right corner (data/video.json → sources, built from the versions and dataset each run recorded), so the close stays clean.

The reform

One parameter: gov.irs.credits.ctc.amount.base[0].amount = 3,000 for 2026-01-01 to 2026-12-31 (current law: $2,200). Static microsimulation, no behavioral responses.

Computed with policyengine.py 6.1.1 (policyengine-us 2.2.1, policyengine-core 3.32.5) on the bundle's default US dataset, hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915 (sha256 6496cc43…). Exact pins: data/compute/requirements.lock.txt.

On screen Value Field
Family, $0 region $0 below $42,200 earnings_sweep.json → gain_starts_at_earnings = $42,206 ($1 search); gain is exactly 0 at every grid point below $42,200
Family, pinned point $50,000 → +$780 earnings_sweep.json → spots["50000"].gain
Family, the reason gain = income tax beyond the current credit's $1,000 nonrefundable share, capped at $1,600 earnings_sweep.json → tax_liability; asserted at all 481 grid points in tools/adapt_outputs.py
Family, full gain +$1,600 from $58,000 full_gain_from_earnings = $57,996; exactly 1,600 at every grid point from $58,000 to $120,000 (and up to $488,000, gain_first_below_full_above_100k = $489,000)
Federal cost in 2026 $31 billion national.json → budget.federal_income_tax_revenue_change = −$31.193B (raw policyengine-us path: $31.269B; shown at whole-billion precision because the two paths differ by 0.24%)
Households that gain 19.2% winners.share_households_gaining_over_1usd = 0.1923
Fewer children in poverty 189,300 poverty.spm.children_under_18.children_lifted_out = 189,306 (Supplemental Poverty Measure); both paths agree to ~1e-9
Average change per household, by decile $6 … $448 … $257 deciles.by_decile[*].average_change_household_net_income
Map dots 12,000 weighted draws of 6,976 distinct households households_sample.json meta (numpy default_rng(20260924), p ∝ household_weight, with replacement)

Independent verification notes: data/verify_national.md, data/verify_statute.md.

Why the family is a curve

A single family at $60,000 gains exactly (3,000 − 2,200) × 2 = $1,600, which needs no model. Swept across earnings, the same family shows where the reform does not change the result: below $42,200 the credit it receives is limited by the 15% earnings phase-in or the $1,700-per-child refundable cap, and its tax liability is too small to absorb any more nonrefundable credit, so raising the maximum adds $0. The gain rises one-for-one with tax liability between $42,200 and $58,000. The national results show the same pattern: the bottom income decile gains $6 on average.

What was left out, and why

  • Congressional districts. data/districts.json holds PolicyEngine's district breakdown (compute_us_congressional_district_impacts in policyengine.py) and data/district_layout.json a hex cartogram, but the national sample gives a median effective sample size of about 20 households per district; the spread within states ($105 sd) exceeds the spread between states ($58 sd), so a district map would mostly show sampling noise. The district-calibrated populace_us_2024_acs_local dataset (1.6M households) is the right source for that scene; it was not run for this cut (several hours of compute for a 1.6M-household file).
  • Household count. The model's weighted count (124.6M) was not checked against Census, so the caption states scope ("Now all 50 states and DC.") and no per-dot household count appears.
  • The 2021 comment. Line 17 of the parameter file ("Rose to $3,000/$3,600 in 2021. See arpa.yaml.") stays visible and unemphasized: the 2021 credit was also fully refundable, so highlighting it would imply an equivalence the two policies do not share.

Invariants

These hold for every input, and bun run test plus CI check them (tests/, .github/workflows/ci.yml):

Property Kind Test
The family's gain is income tax beyond today's $1,000 nonrefundable share, capped at $1,600, at all 601 computed earnings points accounting identity test_family_curve.py
0 ≤ gain ≤ 2 × ($3,000 − $2,200); gain never falls as earnings rise below $150,000; the refundable part never exceeds 2 × $1,700 bounds, monotonicity test_family_curve.py
The change in net income equals the change in the CTC; the sweep at $60,000 equals the separate household run; policyengine.py agrees with the sweep at random earnings (slow, local) differential test_family_curve.py
The plotted points are exactly the computed points; "$0 below $42,200" and "+$1,600 from $58,000" hold at every point differential, exhaustive test_family_curve.py
Every stat and decile bar equals the national run at its stated precision; the reform card equals the dict PolicyEngine received; the code panel, statute and quote are verbatim; every dollar figure in the family text traces to the data differential test_published_numbers.py
Rebuilding video.json from data/ is a no-op round-trip CI
Easings map 0→0 and 1→1 and (except the intended outBack overshoot) stay in [0, 1] and never decrease; progress and envelopes stay in [0, 1]; the seeded PRNG is deterministic and in [0, 1); number formatting round-trips property-based (fast-check) util.test.js
A fresh page and a page that played the film up to t draw identical pixels, in both layouts determinism tools/determinism.mjs, CI
Panning keeps power; synthesis is bit-identical across runs; the master is −14 ± 0.5 LUFS with true peak ≤ −2 dBTP as its 4× meter reads it (about −1.8 at 16×) and clean edges property-based (Hypothesis), mastering test_soundtrack.py
The limiter only catches stray peaks: no 10 ms window cut by more than 3 dB and at most 1% by more than 1 dB, and no felt-mallet cue (lock, ping, statistic, plink, decile) starts in a window cut by more than 1 dB; every swoosh sits 6 ± 0.3 LU under the music after the master bounds, measured on the returned audio test_soundtrack.py
The drum soft clip returns a signal that never reaches its knee bit for bit, and touches nothing further than the resampling filter's reach from a peak; an impact's room equals an independent piecewise reference (3 ms ramp, exact depth where it lands, exponential return, exactly 1 after ten release times) property-based (Hypothesis), differential test_soundtrack.py

How it's made

  • site/ — a deterministic HTML/canvas timeline. window.renderAt(t) draws the frame at time t; nothing depends on wall-clock time or Math.random.
  • tools/render.mjs — Playwright drives headless Chromium frame by frame. --stream out.mp4 pipes PNG screenshots straight into parallel ffmpeg encoders and joins the segments, so no frames touch the disk.
  • tools/soundtrack.py — the score is synthesized with numpy/scipy at 120 BPM (D minor; Bb → C → F under the wordmark), mastered to −14 LUFS integrated with a 4× oversampled true-peak limiter at −2 dBTP. The loud transients are shaped where they are made, so the limiter only shaves strays: the drum bus is soft-clipped (4× oversampled, found through the master's high-pass) with kick and clap eased back on the backbeats, and the hit and drop smoothly dip themselves and the music (and the drums for longer) instead of rippling the whole mix with the limiter. Sound effects come from events.json, which the page exports from the same timeline, so each click and soft mallet note lands on the frame that causes it; gainer notes rise in pitch with the $800 step, and the family curve brightens a held chord as the gain rises.
  • tools/compare_revisions.mjs — renders a film from this checkout and from another (in CI, the PR's base) every 0.2 s in both layouts and compares them. On pull requests CI runs it for the US film, so work on another country cannot move a US pixel; a PR meant to change the US film carries the us-film-changes label.
  • tools/determinism.mjs — proves a fresh page and a page that played the film up to t render identical pixels at 11 timestamps, in both layouts. Streaming workers start mid-film, so this is what makes parallel rendering safe.
  • tools/adapt_outputs.py → tools/build_video_data.py — turn the raw PolicyEngine outputs into data/video.json, the only data the page reads. If any input is missing the page paints a striped MOCK DATA banner on every frame.

Rebuild

bun install
bun run data          # PolicyEngine outputs in data/ -> data/video.json (asserts every on-screen claim)
bun run build         # site/main.js -> site/bundle.js
bun run score         # timeline cues -> audio/events.json -> audio/score.wav
bun run determinism   # fresh-page frames == played-through frames, both layouts
bun run render        # 4K/60 master, 1080p/60, 9:16 (supersampled from 2x), posters
bun run check         # specs, loudness, true peak, single-frame glitch scan
bun run test          # invariants (Vitest + fast-check, pytest + Hypothesis), US and UK

UK: uv run --no-project --with pyyaml python tools/uk/build_uk_video.py asserts every on-screen UK statement and writes data/uk/video.json; the score comes from node tools/render.mjs --country uk --stills 12 --out frames/probe --events audio/uk/events.json and then tools/soundtrack.py audio/uk/events.json audio/uk/score.wav, and tools/render_all.sh uk renders.

Recomputing the PolicyEngine outputs themselves (data/compute/*.py) needs the pinned environment in data/compute/requirements.lock.txt and the dataset from Hugging Face; each script's docstring gives its run command.

Preview any moment in a browser: node tools/serve.mjs 4317, then open http://127.0.0.1:4317/site/index.html?t=17.5 (or ?play=1, or ?w=1080&h=1920 for portrait, or &country=uk).

Credits

  • PolicyEngine wordmark (site/logo-*.svg): from PolicyEngine/policyengine-app-v2. The PolicyEngine name and logo are PolicyEngine trademarks and are not covered by this repository's licenses.
  • data/base.yaml: verbatim from PolicyEngine/policyengine-us (policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml) at commit 2fbd777, AGPL-3.0.
  • Statute text (data/statute.json): 26 U.S.C. § 24 from the Office of the Law Revision Counsel (uscode.house.gov), a US government work, cross-checked against the Legal Information Institute, Cornell Law School (law.cornell.edu). Fetched pages are not redistributed; statute.json records their URLs and sha256.
  • Federalist No. 62 (1788, public domain) as published by the Avalon Project, Lillian Goldman Law Library, Yale Law School; the 1788 date is from Founders Online (National Archives).
  • Microdata: PolicyEngine populace-us (populace_us_2024.h5@populace-us-2024-spm-20260915, MIT, huggingface.co/datasets/policyengine/populace-us). data/households_sample.json holds derived fields for 6,976 of its household records.
  • Congressional district boundaries (data/district_geography.json, used to place dots): US Census Bureau cartographic boundary file cb_2024_us_cd119_20m (public domain), via PolicyEngine/policyengine-app-v2.
  • Congressional district hex layout (data/district_layout.json; kept for the record, not shown in the video): House hexmap v3.1 by Daniel Donner, Daily Kos Elections / The Downballot (the-downballot.com; original release dkel.ec/map), via PolicyEngine/snap-district-map and PolicyEngine/policyengine-app-v2, licensed CC BY 4.0. Changes: district IDs re-keyed to PolicyEngine GEOIDs, centroids and bounding boxes added; polygons unmodified.
  • UK version (data/uk/, details and checks in data/uk/README.md):
    • data/uk/amount.yaml: verbatim from PolicyEngine/policyengine-uk (policyengine_uk/parameters/gov/hmrc/income_tax/allowances/personal_allowance/amount.yaml) at commit 412b25aa, AGPL-3.0.
    • Statute text (data/uk/statute.json): Income Tax Act 2007, Part 3, and Finance Act 2021 s. 5, from legislation.gov.uk. Contains public sector information licensed under the Open Government Licence v3.0.
    • Edward VI, "Discourse on the Reformation of Abuses" (1551, public domain), as printed in J. G. Nichols (ed.), Literary Remains of King Edward the Sixth, vol. 2 (Roxburghe Club, 1857), p. 486, via the Internet Archive.
    • UK national figures: Department for Work and Pensions. (2026). Family Resources Survey, 2024-2025. [data collection]. UK Data Service. SN: 9563, DOI: http://doi.org/10.5255/UKDA-SN-9563-1 (© Crown copyright; UK Data Service End User Licence), as enhanced by PolicyEngine's policyengine-uk-data 1.56.16 (enhanced_frs_2024_25), which also imputes from the Wealth and Assets Survey, the Living Costs and Food Survey, the Effects of Taxes and Benefits data and HMRC's Survey of Personal Incomes (see data/uk/README.md). The data creators, depositors and the UK Data Service bear no responsibility for the analysis here. Only aggregates are published; no record-level data is in this repository.
    • Westminster constituency boundaries, July 2024 (data/uk/geography.json, simplified), and the ONS region and country lookups. Source: Office for National Statistics licensed under the Open Government Licence v.3.0. Contains OS data © Crown copyright and database right 2024.
    • The UK family curve is computed from single households defined in tools/uk/uk_family.py. The national figures come from the enhanced Family Resources Survey 2024-25 (policyengine-uk-data 1.56.16) through policyengine.py 6.2.0; the FRS is UK Data Service data under its End User Licence, so only aggregates are committed.
  • Fonts: Inter, JetBrains Mono and Newsreader (SIL Open Font License, via Fontsource). US state shapes: us-atlas (Census cartographic boundaries). The score is synthesized by tools/soundtrack.py.

License

Code in this repository is released under the MIT License. Original text and figures are released under CC BY 4.0 with attribution to PolicyEngine. Third-party material keeps its own terms (see Credits): data/base.yaml and data/uk/amount.yaml (AGPL-3.0), the UK statute text and boundaries (Open Government Licence v3.0), data/district_layout.json (CC BY 4.0, Daily Kos Elections / The Downballot), and the PolicyEngine name and logo.

About

PolicyEngine in 30 seconds: the Child Tax Credit from statute to code to a family to the nation, with every statistic computed by PolicyEngine

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages