📊 Scenario: Data That Wants to Be a Story
You have a spreadsheet: country, year, population. They’re just numbers until D3 turns them into bars that grow, change position, and reveal four decades of geopolitical drama. The viewer doesn’t read the table — they watch it happen.
Why D3 for this project?
D3 is the only library in the stack with native scales (linear, band, time) and data transition management. You don’t calculate pixels manually—you define the domain and range, and D3 handles the geometry. GSAP would animate the visuals, but it wouldn’t understand the data.
✓ D3 is ideal when…
✗ D3 isn’t the best choice when…
🏃 Bar Race: the heart of the project
On each Remotion frame, the dataset for the corresponding year is read, the bars are reordered, and the Y positions are smoothly interpolated. When China and India switch places, the bar race pause for 12 frames — the dramatic moment.
💡 The dramatic pause
When a change in leadership occurs, add 12 frames pause before reordering. It's the cinematic equivalent of a close-up — it gives the viewer time to process what just happened.
📐 D3 Scales in the Remotion Context
The D3+Remotion integration works because D3 calculates geometry and Remotion advances time. With each call to useCurrentFrame(), you compute the corresponding data frame and let D3 map the pixels.
const frame = useCurrentFrame(); const fps = useVideoConfig().fps; const year = Math.floor(interpolate(frame, [0, durationInFrames], [1990, 2030])); const data = getDataForYear(year); const xScale = scaleLinear().domain([0, maxPop]).range([0, width]); const yScale = scaleBand().domain(data.map(d => d.country)).range([0, height]);
🏷️ Labels, Year, and Readability in Motion
Labels follow the bars; the year counter in the lower-right corner anchors viewers in time. Both need to be calculated in the same frame — without separate state.
✓ Labeling best practices
✗ Common mistakes
💡 Year counter as an anchor
The large year in the corner is the viewer’s compass. Minimum size: 64px, opacity 0.5–0.7 so it doesn’t compete with the bars. Use font-variant-numeric: tabular-nums to prevent the digits from “jumping” when changing.
💬 The Complete Prompt
This is the exact prompt you give Claude Code. Each instruction maps to a specific bar race behavior — Claude generates the Remotion composition with D3 integrated in one response.
Create an animated bar race showing the ten most populous countries from 1990 to 2030. Animate transitions smoothly. Display ranking changes in real time. Pause briefly when leadership changes.
💡 Why "Pause briefly when leadership changes"?
This specific instruction makes Claude implement the dramatic pause. Without it, the reordering happens continuously and goes unnoticed. With it, the viewer gets the cinematic moment of “wait, India passed China!”
♻️ Deliverables and Reusability
The deliverable is a YouTube-style infographic video—but the component is a parameterized template that works with any time-based ranking dataset. Swap the spreadsheet, change the title, render another video.
Build order: from spreadsheet to video
Prepare the dataset
JSON array of objects {country, year, population}. Data for 1990, 2000, 2010, 2020, 2030. Interpolate the years in between.
Build scales and stills
Validate that the bars appear at the correct size without animation. Confirm that the 1990 ranking is correct.
Connect the frame to time
Use interpolate() to map frame → year. Add a pause for years when leadership changes.
Polish: colors, typography, music
Add a visual identity for each country, a bold font for the numbers, and dramatic music (Track 4).
📋 Module 3.2 Summary
- ✓D3 turns data into a story — automatic scales, dynamic sorting, transitions
- ✓Dramatic pause when the leader changes = cinematic moment in the bar race
- ✓Labels follow the bars; the year counter is the viewer’s time anchor
- ✓Persistent color by country lets you follow a country throughout the entire video
- ✓Deliverable: reusable template for Finance, Sports, Politics, Economics
Next module:
🏍️ Module 3.3 — Motorcycle Commercial (Three.js) →