Communitygithub.com

ErtanLabs/ertanlabs-motion

Direct, render and verify motion graphics with a deterministic HTML-to-video pipeline. Each piece borrows its visual grammar from a real "source world" artifact, records every choice in a decision ledger, plans timing in a frame-numbered beat sheet, renders frame-exact MP4, ProRes (with alpha) or WebM through a bundled parallel renderer, and is measured by a bundled checker for holds, loudness, flashes, colour tags and platform safe zones. Use for kinetic type, explainers, narrated video essays and paper-collage documentaries (the look people call "Vox style"), cinematic showreels and brand films, title sequences, animated charts, maps, globes and flight routes, product promos, lower thirds, counters, social verticals (Reels, Shorts, TikTok) and editor overlays, for Remotion or HyperFrames projects, and whenever someone wants an idea, script or dataset turned into a short video, even when the words "motion graphics" never appear.

What is ertanlabs-motion?

ertanlabs-motion is a Claude Code agent skill that direct, render and verify motion graphics with a deterministic HTML-to-video pipeline. Each piece borrows its visual grammar from a real "source world" artifact, records every choice in a decision ledger, plans timing in a frame-numbered beat sheet, renders frame-exact MP4, ProRes (with alpha) or WebM through a bundled parallel renderer, and is measured by a bundled checker for holds, loudness, flashes, colour tags and platform safe zones. Use for kinetic type, explainers, narrated video essays and paper-collage documentaries (the look people call "Vox style"), cinematic showreels and brand films, title sequences, animated charts, maps, globes and flight routes, product promos, lower thirds, counters, social verticals (Reels, Shorts, TikTok) and editor overlays, for Remotion or HyperFrames projects, and whenever someone wants an idea, script or dataset turned into a short video, even when the words "motion graphics" never appear.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ErtanLabs/ertanlabs-motion/tree/HEAD/skills/ertanlabs-motion

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

ErtanLabs Motion

Motion work goes generic in a predictable way: every choice a brief leaves open gets filled with whatever is most common, and a video assembled from most-common choices is indistinguishable from thousands of others. This skill makes the open choices explicit, gives each one a traceable source, renders deterministically, and measures the file before anyone sees it.

House rule: every visible choice has an owner. If you cannot point to where a font, colour, curve or sound came from, nobody chose it.

The route

PhaseStepLeaves behind
A · DecideA1 Probe the machine--doctor result, chosen backend
A2 Take the brief apartLogline, canvas, inventory, soundtrack, guardrails, tagged assumptions
A3 Find the source worldThe artifact whose grammar the piece borrows, story device, hero beat
A4 Write the default, then overturn itThe most-common version and its replacements
A5 Keep the ledgerledger.md beside the composition
B · MakeB1 Plan in framesBeat sheet with planned holds
B2 Weigh every elementWeight classes, lead and follow
B3 Let the world set type, colour, soundLedger rows filled from the source
B4 Proof, then renderInspected hero frames, then the file
C · ProveC1 Measure and lookcheck.py report, contact sheet, tells index
C2 Tag claims and hand offEvidence-grouped handoff

Working modes

  • Sprint (short single-idea pieces, or "just make it"): choose the source world yourself, write a five-line ledger (logline, world, hero beat, three replaced defaults, canvas), build. Proof and measurement still run.
  • Studio (brand work, longer pieces, unclear briefs): render the hero frame for two candidate source worlds with --stills and show the two images side by side; people judge pictures faster than descriptions. The user picks; with nobody available, keep the one whose hero frame survives the thumbnail squint (C1) and note why in the ledger.
  • Series (episodes for one channel or campaign): read every earlier ledger.md in the project first. A new episode may not repeat both the previous source world and the previous hero-beat structure; brand constants carry a fixed tag and are exempt.
  • Revision (notes on a delivered piece): map each note to beat-sheet rows and write the change as a beat-sheet diff, old row → new row. Example: "too slow at the start" → grid 6-30 → 4-16, hold 30-60 → 16-40, every later row shifts by −20 frames. Render only the changed range (--range) for review, then the full piece, and append the diff to the ledger.
  • Narrated explainer (video essays, documentaries, history, money, crime or science stories driven by voice-over, paper-collage looks): the narration is the clock and every sentence becomes a beat. Two rules carry most of the quality: a hold ends with its sentence, and every fact on screen is either spoken or sourced on a slip. Read references/paper-collage-explainer.md and start from assets/collage-template.html.

Production level

Independent of the mode, every piece has a level, written in the ledger:

  • Graphic (default): the source world's grammar on a flat canvas. Right for overlays, lower thirds, UI pieces, data hits and anything an editor composites.
  • Film: the artifact in its place, seen through a camera, lit by a source the world explains, with materials, life between the beats, three or four scenes and designed sound with music. Right for showreels, landing pages and repository front pages, brand films, launches, map and route showcases, and whenever someone asks for "cinematic", "lively", "premium" or "less flat". Read references/film-level.md before planning; it adds a scene list above the beat sheet and its own checks.

Phase A · Decide

A1 Probe the machine

node scripts/render.mjs --doctor

The output decides the backend: Node, Playwright or an installed Chrome/Edge, ffmpeg, ffprobe, NVENC. Report it to the user in one line. If the HTML path is unavailable, B4 lists the alternatives; if the user already has a Remotion or HyperFrames project, work inside it.

A2 Take the brief apart

  1. Logline: the single sentence the video says. Without one, the piece turns into decoration; draft one and confirm it.
  2. Canvas: ratio, length, fps, destination. Sizes and safe zones: references/formats-and-safe-zones.md.
  3. Inventory: footage, screen captures, logos, photos, data, AI plates. Existing material shapes the concept rather than being laid on top of it.
  4. Soundtrack: voice-over, music or none. With VO or music, the audio sets the timing (references/sound-and-sync.md). Audio the user supplies is final: never add or remove pauses, cut, stretch or re-time it without the user's yes; adjust the picture instead.
  5. Guardrails: brand fonts and colours, required and forbidden elements, deadline.

Unknowns become assumptions in the ledger, tagged assumed. Ask a question only when the answer would change the source world.

A3 Find the source world

Do not pick "a style". Pick a real artifact from the subject's world and borrow its grammar: a tide table, a pharmacy leaflet, a split-flap departure board, a seismograph roll, a redacted dossier, an editor's desk of clippings. One artifact settles several decisions together - the type it is set in, the inks and materials that give the palette, the way things change in it (flip, print, stamp, fill, plot), its surface - so the decisions agree with each other.

  1. List five artifacts that people who live with the subject actually handle or read.
  2. Prefer the one with motion built in: something that flips, prints, fills, plots, stamps or scrolls by itself.
  3. Borrow rules, not pictures. Not a photo of a ticket behind the text, but the ticket's logic: a perforation separates, a serial number counts, a stamp cancels.

references/source-worlds.md describes 31 worlds grouped by the way they move. Treat it as calibration; the best world for a job is often not on the list.

Name two more things:

  • Story device: one sentence on how the logline becomes visible ("the tide curve plots and the water in the frame follows the pen").
  • Hero beat: the beat that carries the logline. It gets the longest planned hold and the largest change of state or scale in the piece. After rendering, the longest hold check.py measures should be the hero's; if it is not, the emphasis is in the wrong place. Under a voice-over the hold still ends with its sentence and the pause after it. If the hero needs more time, ask for a longer pause while the voice is still being written or recorded; with a finished voice-over, make the hold fit, and never hold the picture over the next line.

A4 Write the default, then overturn it

The first idea is usually the most common one. Make it visible, then replace it:

  1. Write the default version of this video in one honest line: what an average model would make from this prompt.
  2. Choose at least three decisions in that line (type, palette, layout, entrance, transition, sound) and write a replacement for each with its source: the source world, the material, the data or the soundtrack.
  3. A default you cannot replace with a reason may stay; it goes into the ledger marked owned default.

A5 Keep the ledger

Fill it before building, show it, and save it as ledger.md beside the composition. It is also the memory for the next episode.

LEDGER · <project> · <date>
Logline:
Canvas: 1080×1920 · 30 fps · 8 s · Reels · safe zone: vertical
Source world:
Story device:
Hero beat (frames):
Default version (one line):

| Layer       | Decision                  | Source / reason                 | Default it replaces |
|-------------|---------------------------|---------------------------------|---------------------|
| Type        |                           |                                 |                     |
| Palette     | hex values + job of each  | sampled from ...                |                     |
| Layout      |                           |                                 |                     |
| Time base   | fps, weight classes, holds|                                 |                     |
| Texture     |                           |                                 |                     |
| Sound       |                           |                                 |                     |
| Transitions |                           |                                 |                     |

Rights (fonts, music, data, imagery):
Assumptions:

Phase B · Make

B1 Plan in frames

A beat sheet is a compact exposure sheet: motion written as data before code. Ranges are [in, out) in frames; holds get their own rows.

BEAT SHEET · 30 fps · 240 frames (8 s) · 1080×1920
| Beat | Frames   | Element     | Class     | Action                          | Carries | Sound        |
|------|----------|-------------|-----------|---------------------------------|---------|--------------|
| 1    | 6-30     | grid        | mark      | lines plot, staggered           | enter   | pen          |
| 1    | 30-60    | -           |           | HOLD                            |         |              |
| 2    | 60-168   | tide curve  | set piece | plots; water follows the tip    | link    | low swell    |
| 2    | 168-198  | -           |           | HOLD (hero)                     |         | silence      |
| 3    | 198-200  | headline    | stamp     | lands in the water              | land    | one knock    |
HOLDS: 30-60, 168-198, 200-240  →  planned hold ratio 42%

Carries names what the viewer gains: an element enters, two things link, a state changes, or a beat lands on the soundtrack. An empty Carries cell marks decoration; cut it.

Hold ratio is the share of frames in which nothing meaningful moves. Explainers and title work usually sit around 20-35%, music-cut montages lower, contemplative pieces higher. Grain and slow sub-2 px drift are texture, not movement. check.py measures the ratio so plan and result can be compared.

With VO or music, convert word and beat timestamps to frame numbers and write them into the sheet. Under a voice-over each beat also gets a cue, its first stressed word: the beat's main element is in place by the cue, and no hold runs into the next sentence.

B2 Weigh every element

Give every element a weight class; the class chooses its curve and duration from the set in references/motion-and-timing.md, so durations differ because masses differ, not by taste.

ClassTypical elementsArriveLeaveCurve
propdots, tags, small numbers6-12 f4-8 fsettle in, launch out
paneltitles, cards, chart blocks14-24 f10-16 fheavy
set piececamera, water planes, maps, large fields30-90 f20-45 fheavy; linear for machinery
marklines, routes, underlines, handwritinglength ÷ ~30 px per framefadespen
stampcancels, final values, cuts1-2 f-step
  • Lead and follow. At any moment one move leads; any other move is caused by it (the pen reaches the extreme, so its label opens).
  • Dependency order. An element appears only after what it depends on is on screen: an axis before its data, a route before its marker.
  • Poster frame. Frame 0 is often the thumbnail, so compose it as a finished, readable image and let motion grow from it.
  • Frame-pure. Every value is computed from the frame number alone: no wall-clock CSS, timers or unseeded randomness (references/composition-contract.md).

B3 Let the world set type, colour, sound

  • Type is inherited from the source world. Emphasis has to survive the thumbnail squint (C1): a word marked only by hue disappears at small size in greyscale, so emphasise with size, weight, placement or timing. Set <html lang> and use locale-aware casing; render.mjs glyph-tests the text actually on the page. Details: references/typography.md.
  • Colour: every colour has a named job (ground, type, signal, a data series). A colour without a job is removed. Sample values instead of inventing them: python scripts/check.py --palette reference.jpg. Details: references/color-and-texture.md.
  • Sound statements are only ever Measured or Not verified, because audio cannot be inspected by eye. Every entry in the Sound column points at a visible cause or at the bed. Details: references/sound-and-sync.md.

B4 Proof, then render

BackendWhen
HTML + render.mjsDefault whenever --doctor passes. Start from assets/composition-template.html (or assets/collage-template.html for narrated explainers); contract in references/composition-contract.md.
Remotion / HyperFramesThe user already works in one. Same ledger and beat sheet, different syntax: references/backends.md.
Artifact previewOnly if the environment offers a motion or video artifact type, and only as a preview; a deliverable is still a rendered file.
PlatesShots code cannot draw convincingly (people, real places in motion) come from footage or a video model as plates without text; everything typographic stays in the composition. references/backends.md.

Maps, globes and routes: also read references/maps.md (§7a for showcase routes).

Proof the hero beat before building the rest:

node scripts/render.mjs comp/index.html --stills 0,170,last             # poster, hero, end frame
node scripts/render.mjs comp/index.html --range 150-210 --scale 0.5     # hero beat at half size
node scripts/render.mjs comp/index.html -o comp/out.mp4                  # full render
node scripts/render.mjs comp/index.html --alpha -o comp/overlay.mov     # ProRes 4444 with alpha

Open proof images with your image tool before going on; a file you have not looked at is not Inspected. Overlays for editors ship with an alpha channel (--alpha).

Phase C · Prove

C1 Measure and look

python scripts/check.py comp/out.mp4

The checker reports resolution, fps, frame count, colour tags, faststart, integrated loudness and true peak, silences, holds and hold ratio, a rough flash estimate and suspected corrupted frames, and writes <name>_check/contact.png: six evenly spaced frames with the platform's UI bands shaded. Then:

  1. Score the contact sheet with references/default-tells.md (tells index).
  2. Topic swap: mentally replace the subject with an unrelated one. If the frames would still fit, they were not derived from this subject; go back to A3.
  3. Thumbnail squint: view the contact sheet at about a tenth of its size. The hero beat should be the first thing you find, and emphasis should still read.
  4. Hold audit: compare measured holds with the planned ones. A missing hold means something is still moving; a longest hold outside the hero beat means misplaced emphasis.
  5. Cue check (voice-over or music cues): python scripts/check.py comp/out.mp4 --at <cue seconds> writes cues.png, the exact frame at each cue. Every one must show what is being said or hit at that moment.

Accessibility is a gate, not a score: at most three flashes in any one second (WCAG 2.3.1), and text contrast of at least 4.5:1, or 3:1 for large display type (WCAG 1.4.3).

C2 Tag claims and hand off

Every claim in the handoff carries one of four tags:

  • Rendered: the file was produced in this session.
  • Inspected: these specific frames were opened and looked at.
  • Measured: a number from check.py, ffprobe or ffmpeg.
  • Not verified: anything else, stated plainly.

A claim that cannot carry one of the first three tags is reported as Not verified, including anything about how audio sounds and anything about a reference you could not open. Charts drawn without a data source say "schematic" on screen.

HANDOFF · <file>
Spec (Measured): codec, size, fps, frames, duration, colour tags, loudness
Rendered: <files>
Inspected: <frames looked at>
Measured: holds, hold ratio, warnings resolved or accepted
Not verified: <one line>
Defaults replaced: <ledger rows that replaced a default, with their reason>
Tells index: <n> · accepted exceptions
Next: <one step, optional>

Toolbox

FileReach for it when
references/source-worlds.mdChoosing a source world: 31 artifacts grouped by how they move, including colour-and-light and navigation worlds
references/film-level.mdFilm-level pieces: space, camera, light, materials, life between beats, scene lists, sound design and music, render budget
references/paper-collage-explainer.mdNarrated explainers and documentaries: narration-first beats, collage desk grammar, beat recipes, real-people rules, optional plates
references/motion-and-timing.mdEasing palette, weight classes, seconds to frames, holds, camera, joining beats, short-form rhythm
references/typography.mdChoosing and testing type, sizes, numerals, locale rules, glyph tests, subtitles, licensing
references/color-and-texture.mdColour jobs, sampling, contrast, matching footage, texture recipes and their bitrate cost, sRGB to BT.709
references/sound-and-sync.mdLocking to VO and music, the sound map, placing effects, ducking, loudness targets
references/formats-and-safe-zones.mdCanvas sizes, safe zones, multi-format delivery, export presets, cover frames
references/maps.mdMap data and licences, projections, routes, zoom, labels, relief without GDAL, globes, lifted flight arcs, computed telemetry
references/composition-contract.mdThe COMPOSITION contract, template helpers, media, libraries, running the renderer, fixing bad frames
references/backends.mdRemotion, HyperFrames, artifact previews, editor handoff, plates from video models
references/default-tells.mdTells grouped by how they are detected, with weights and usual fixes
assets/composition-template.htmlStarting point for a new composition (copy, then edit)
assets/collage-template.htmlStarting point for a narrated paper-collage beat, with the cut-paper kit built in
scripts/render.mjsDeterministic parallel render into one continuous stream, stills, alpha, glyph test, doctor
scripts/check.pyPre-publish report, contact sheet, cue frames (--at), loudness, holds, flashes, corrupted frames, palette extraction
scripts/glyph_check.pyGlyph coverage of a font file for given text
scripts/paper_cutout.pyTurn a supplied photo into a cut-paper cutout with a rim and contact shadow
scripts/music_bed.pyGenerate an instrumental music bed locally with MusicGen on a CUDA GPU (weights CC BY-NC 4.0: non-commercial)
examples/tide-table/A finished job: intake, ledger, beat sheet, code, check results, handoff

Related Skills