# Kinetic words

Words pop in one after another; *starred* words take the accent colour and get underlined. 5 s, 16:9.

- Category: kinetic-text · Tags: typography, text, quote, hook
- Spec: 1920×1080 @ 30 fps · 5s (16:9)
- License: CC0 · Tier: free
- JSON: https://gimgs.net/motion/kinetic-words.json
- Kind: html
- Template HTML: https://gimgs.net/motion/kinetic-words/template.html
- Player: https://gimgs.net/motion/kinetic-words/play?p=<base64url JSON params>[&t=<seconds>]
- Credits: gimgs — https://gimgs.net/motion
- Page: https://gimgs.net/motion/kinetic-words.html

## Params

| name | type | default | description |
|---|---|---|---|
| `text` | string | `"Ship *faster* than your *competitors*"` | The sentence. Wrap a word in *asterisks* to highlight it. ~3–9 words works best |
| `accent` | string (color) | `"#ffb224"` | Highlight colour |
| `fg` | string (color) | `"#ffffff"` | Text colour |
| `bg` | string (color) | `"#111111"` | Background |
| `size` | integer | `150` | Font size in px (auto-wraps) |
| `stagger` | number | `0.12` | Seconds between consecutive words |
| `startAt` | number | `0.4` | Delay before the first word (seconds) |

Default params (base64url for `?p=`): `eyJ0ZXh0IjoiU2hpcCAqZmFzdGVyKiB0aGFuIHlvdXIgKmNvbXBldGl0b3JzKiIsImFjY2VudCI6IiNmZmIyMjQiLCJmZyI6IiNmZmZmZmYiLCJiZyI6IiMxMTExMTEiLCJzaXplIjoxNTAsInN0YWdnZXIiOjAuMTIsInN0YXJ0QXQiOjAuNH0`

## Prompt

## When to use
A hook or a quote for social video, a section title, a punchline. One idea per clip.

## How to adapt
- Put the sentence in `text`; star the 1–2 words that carry the meaning (`*faster*`). Everything else stays in `fg`.
- Total run time before the last word lands = `startAt + (words − 1) × stagger + 0.55`. Keep it under `duration` (5 s) or the tail is cut; for 12+ words lower `stagger`.
- Long sentences: lower `size` to 110–120 so the block fits two or three lines.
- Chain several clips with different `text` for a whole script; keep `accent`/`bg` identical for continuity.

## Do not change
`window.motion`, `#stage` size, `animation-fill-mode: both`. The word spans are rebuilt on every `apply()` — that is expected.

## Flow

1. **Write the line**
   Short. Star the key words.
2. **Render**
   `frames 0…149 at t=i/30 → ffmpeg -framerate 30 -i frame-%04d.png -c:v libx264 -pix_fmt yuv420p out.mp4`
3. **Chain**
   `ffmpeg -f concat -safe 0 -i list.txt -c copy script.mp4`

## Render

Open frame_url in a headless browser with viewport width×height (deviceScaleFactor 1), wait for document.documentElement.dataset.motionReady === '1', screenshot; repeat for t = i/fps (i = 0…frames-1); then `ffmpeg -framerate <fps> -i frame-%04d.png -c:v libx264 -pix_fmt yuv420p out.mp4`. Or drop ?t to play it live.

## Contract

Self-contained HTML. Declares window.motion = { width, height, fps, duration, params, apply(params) [, seek(t)] }; root element #stage sized width×height; animations are CSS/WAAPI with animation-fill-mode: both so any t can be seeked via document.getAnimations(). The host runtime (injected by /play) merges ?p params, calls apply(), scales #stage to the viewport and, when ?t is given, pauses and seeks deterministically.
