Isometric Infographic — our demo

One example among many. Don't reuse its story, arc, shots, props or timings.

Demo: From Bean to Cup (58.8 s) · iso-infographic.mp4 · source in demo/

The demo follows one coffee bean from a farm to a cup in a 45–60 s film: one isometric map, one camera take, a Z-shaped route across a square board.

Story & structure

The whole film is one isometric map: a square diorama board that projects to a rhombus of almost exactly 16:9. Every step of the process is a station on that board. A dashed route line connects the stations. The camera never cuts. It rides the route with pushes, pulls, pans and follows, and each station is told the same way: one action, one label, one number built from icons.

What makes it work: - Flat, true isometric. No perspective, no outlines. Shapes are separated only by the three tones of each face. - Infographic furniture is part of the picture. Leader lines, label pins, cut lines, hatched section faces, tracking rings and a legend all live on the map. - One tracked subject. A single object (a bean, a parcel, a drop of water) is followed through the whole system with a "you are here" ring. That turns a diagram into a story.

How the demo used the medium:

Native power Story use
One-take diorama Any process becomes a route across one board. Lay the route out as a Z across a square board (far corner → right → across the middle → near corner). A straight strip wastes the final pull-back, but a square board fills 16:9 exactly.
Cutaway One unified gesture: a dashed cut line draws, the near skin slides out along its normal and fades, and hatched section rims remain. Use it for a fruit, a ship's hull, a container, a sack, a building or a machine.
Isotype numbers Every station gets a number drawn as identical icons, with a different arrangement each time: a 3×7 calendar of suns, 14 container icons (1 = 1,000), a day/night row, a rising thermometer, 70 beans that arrange into the silhouette of a cup, and an 11-cell distance bar.
Focus + context At the emotional peak, desaturate everything except the subject (the engine's one-colour hook). This is the infographic's own way of saying "this one".
Frame within the frame A detail callout: a leader line grows from the map and ends in a circular inset showing a close-up. The system and the individual are on screen together.
Scale reveal End by pulling back to the whole board. The route lights station by station and every label is on screen at once, so the last frame is a finished infographic.

Story shape (as in the demo): 1. Open on an extreme close-up of the subject arriving in a hand, with a tag: 0 km. 2. Pull back to the first station and the title. 3. Numbers grow station by station (2 seeds → 400,000 beans per sack → 14,000 containers). The subject gets lost. 4. At the busiest point, dive through nested cutaways and find it: the silence, and the only colour left. 5. The loudest sound in the film breaks the silence, and the camera snaps back out. The time jump is hidden inside the dive. 6. Transformation stations, a second silence, then the gentle payoff. 7. Echo: the same close-up composition, a different hand and a different object, and the tag now reads the total. Pull back to the full map.

The numbers must add up. This style lives on credibility: 400 + 10,500 + 100 = 11,000 km, and the Isotype bar is coloured in the same proportions. Put the real order of the process on screen (in the demo, beans are shipped green and roasted in the destination country) and say why in your treatment if you change a brief.

Shots

The camera is a world centre plus a log-interpolated zoom k (px per unit). The demo runs from k = 3.75 (whole board) to 8,000 (one bean).

Beat Camera
Open Extreme close-up of the hand (k ≈ 600), drifting slightly. The camera sits 0.17 units above the palm so the branch fits above it.
Title Pull ×35 to the first station (eio in position, log-linear in zoom) while the title plate rises.
Station Hold wide, and open a circular inset for the detail.
Transfer Descend (crane) or follow a vehicle; the next station's sound arrives first.
Busy station Locked, then pull out as the system fills.
Breath A long lateral follow over empty water.
Fast-forward Follow with a wake and a day/night icon row.
Dive Three pushes through nested cutaways, slow → faster → fastest (0.9 s, 0.6 s, 0.6 s) so it reads as a fall. One instrument drops out and the low-pass closes further at each level.
Silence A slow push (k 3,200 → 8,000) while the colour drains from everything but the subject.
Snap-out Pull ×470 in 1.2 s with an ease-out in log zoom. Fade in any tall object that would sweep through the frame during the pull.
Echo A push into the second hand with the identical framing.
End Pull back to the whole board; the end card sits in the empty cream corner.

Frame so the subject is at least 1/3 of the frame height at key moments (the hands, the drum, the bean with its ring), and keep the bottom 170 px clear for subtitles.

Score structure

Palette & props

Titles, subtitles & end card

Pitfalls

Build notes

styles/iso-infographic/demo/
  timeline.js   tempo grid, cue times, subtitles, km numbers (single source of truth)
  engine.js     the style: projection, camera, 3-tone solids, labels, insets, cutaways, Isotype, people, any-shape extrusion
  world.js      the board: coasts, terraced hill, beds, roads, ports, ship, cranes, city, cutaway buildings, hold/containers/sacks
  hero.js       close-ups: palm, picking fingers, branch, cherry, cup, km tag
  film.js       camera path, story state, stations, overlays, subtitles, title/end card, sound events
  music/score.py   original score (sampler)      mix.py   ambience + foley + voice + ducking
  tools/        cuecheck.py subs.py final_asr.py review.sh      tests.js   hero + any-shape test scenes
  1. Write timeline.js and the cue map first. Draw the board as a square in world units (S = 240). Lay out the Z route, then the stations.
  2. Build the station scenes, reviewing each at its zoom with ?k=…&cx=…&cy=… stills. Render the two hero close-ups on their own (?test=hero, ?test=hero&end=1) until they match.
  3. sh demo/build.sh rebuilds everything in about 2 minutes: TTS → whisper → score → events → cue check → mix → srt → render (1,411 frames in about 35 s with 2 workers) → mux (−14 LUFS, grain 0) → final whisper → stills.
  4. Review with bash demo/tools/review.sh out/revN 6 $(seq 0.3 1 58.3) at least twice, plus frame strips of the dive, the snap-out, the landing and the pour taken from the muxed mp4.

Engine reference

demo/engine.js is dependency-free ES module code for Canvas 2D. Everything is queued and depth-sorted until flush().

API What it does
new Iso(ctx, W=1920, H=1080) Engine instance. iso.cam = {x, y, z, k} sets the world point at screen centre and the zoom (px per unit).
iso.P(x,y,z) / iso.unP(sx,sy,z) Project and unproject.
iso.push(ox,oy,oz,rot,s) / iso.pop() Local frame (translate, rotate about z, scale) for vehicles and ships. Normals rotate too, so shading stays correct at any heading.
iso.box(x,y,z,w,d,h,tri,{decal,bias,layer}) Axis box, three-tone. decal(I) draws on its faces (windows, ribs).
iso.prism(pts2d,z,h,tri) Extrudes any footprint polygon. Orientation is automatic and each side face is shaded by its normal.
iso.cyl(x,y,z,r,h,tri,{axis:'z'|'x'|'y', n, spin, stripe}) · iso.cone(...) · iso.roof(...) · iso.sphere(...) Cylinders (standing or lying, spinning stripes), cones, gable roofs, and flat-disc spheres with a lit cap and a dark crescent.
iso.flat(pts,z,color) · iso.line3(pts,color,px,{dash}) · iso.bill(x,y,z,fn) Ground decals, 3D polylines, and billboards (2D drawings scaled by zoom: people, hands).
iso.text3(str,x,y,z,{plane:'xy'|'xz'|'yz', size, extrude, tri}) Isometric type on a world plane, optionally extruded.
iso.desat = 0..1, iso.keep = true The one-colour hook: everything drawn is desaturated toward warm grey except items drawn with keep = true.
iso.sub(fn) Sorted sub-queue, used for interiors drawn between back and front walls.
tri(hex) · PAL Three-tone generator and the palette.
pin(iso,x,y,z,{title,num,unit,dir,up,len,a,icons}) Label pin with reveal parameter a. icons(g,x,y,dir) draws an Isotype row under it.
inset(iso,ax,ay,cx,cy,r,a,drawIn) Circular detail callout with a leader.
cutLine(g,pts,a) · hatch(g,pts) · ring(g,x,y,r,a,t,tag) Cut line, hatched section face, rotating dashed tracking ring with a tag.
ICON.sun/moon/box/sack/bean/cherry(g,x,y,size,lit) · iconGrid(g,x,y,n,lit,cols,gap,draw) Isotype icons in the same three-tone language; lit count m can be fractional (pop-in).
person(g,{body,hat,hair,skin,arm,face,h,basket,apron,tool}) Minimal people for iso.bill.
shape(iso, pts2d, [x,y,z], {color, depth, plane, scale}) Draw any shape in this style: a 2D outline becomes an isometric solid shaded with the three-tone rule. sparkPath(n,r,inner) and cursorPath(h,w,x,y) are ready-made outlines.

Minimal example: a warm-orange #D97757 four-point spark with a cursor tail on a plinth, labelled (?test=spark, output in demo/stills/engine_shape.jpg):

import { Iso, PAL, shape, sparkPath, cursorPath, pin } from './engine.js';
const iso = new Iso(ctx); iso.cam = { x: 0, y: 0, z: 2.5, k: 70 };
iso.box(-6, -6, -1, 12, 12, 1, PAL.cream);                                          // plinth
for (let i = 0; i < 5; i++) iso.box(-5 + i * 2.2, 3.2, 0, 1.6, 1.6, .6 + i * .5, PAL.blue);
iso.keep = true;                                                                    // the one element that keeps its colour
shape(iso, sparkPath(4, 2.2, .22), [-1, -1, 5], { color: '#D97757', depth: .9, plane: 'xz' });
shape(iso, cursorPath(2.4, .45, 2.7, 0), [-1, -1, 5], { color: '#D97757', depth: .9, plane: 'xz' });
iso.keep = false;
iso.desat = 0;          // set 1 to grey everything except the spark (focus + context)
iso.flush();
pin(iso, 0, -1, 7.6, { title: 'ANY SHAPE', num: '#D97757', dir: 1, up: 90, a: 1 });