Free skill

bento-ui-motion-grid

Make a silent, ultra smooth bento grid of UI cards where every card animates on its own. Rendered frame by frame at 120fps with a 60fps cut for X.

Download the skill .zip
Poster frame: a grid of 24 UI cards including a music player, clock, map route, chat, kanban board, timer and color picker
Poster frame from the example render (1440x1080).

Bento UI motion grid (time-driven HTML, 120 fps)

Style tags: bento-cards, ui-mockup, clean-minimal, light-airy, grotesk-led.

Pick this when: you want the viral "this model can actually design" style post: one static camera on a grid of Dribbble-tier product widgets, everything alive at once, buttery motion, no voiceover, no cuts. Great for showing off design taste, a component library, an app concept or a design system in 24 seconds.

Don't pick it for: - camera pushes through one product flow: pastel-glow-ui-film; - cursor-driven tool tours with stickers: figma-motion-launch; - changelog / release cadence films: linear-releases; - fast hard-cut screenshot collages: fast-collage-recolor-demo.

How it differs from the nearest skills

bento-ui-motion-grid (this) pastel-glow-ui-film figma-motion-launch fast-collage-recolor-demo
Camera locked off, never moves big pushes and pans cuts + cursor moves cuts
Motion 24 cards animating at once, each on its own clock one focus at a time cursor-led screenshot swaps
Cuts none (intro springs in, then continuous) scene windows cuts many
Format 1440x1080, 120 fps master, 60 fps cut, 24 s, silent 1920x1080, 50 fps 1920x1080 landscape
Tech single index.html, pure draw(t) Remotion DOM/canvas DOM

Visual spec (template defaults)

Architecture (template/)

index.html            everything: CSS tokens, easing + timing helpers, roll() odometer digits, 24 widget functions W.<name>(t),
                      the layout table L = [[id, x, y, w, h, 'w'|'d'|''], ...] and draw(t) which re-renders the whole page for time t.
scripts/render.py     parallel Playwright workers; frame i -> draw(i/fps) -> screenshot 2.8125x -> 1440x1080 PNG. --fps --seconds --workers
scripts/snap.py       stills at chosen seconds into out/ (fast review loop)
scripts/smoothness.py per-card frame-diff spike detector (finds snaps and cycle-wrap jumps)
scripts/encode.sh     frames -> out/grid_120.mp4 (120 fps master) + out/grid_60.mp4 (every 2nd frame, for X), ffprobe check

Timing helpers (the whole motion language)

Quick start

cp -r skills/bento-ui-motion-grid/template my-grid && cd my-grid      # never build inside the skill folder
pip install playwright pillow numpy && python3 -m playwright install chromium   # or pass --chrome /usr/bin/google-chrome
python3 scripts/snap.py 0.6 3 7.5 12                                  # look at the intro and a few mid states
python3 scripts/render.py --fps 120 --seconds 24                      # 2880 frames, ~4 min on 6 workers / 8 cores
python3 scripts/smoothness.py frames                                  # expect BIG SPIKES near 0; inspect any that remain
bash scripts/encode.sh frames out                                     # out/grid_120.mp4 + out/grid_60.mp4

Recipe

  1. Concept first. Pick a fictional product or a mixed "design system" set and ONE accent colour. Do not copy a reference app's widgets; vary the widget types (media, time, money, map, chat, tasks, controls, status).
  2. Layout. Edit L. Keep 5 columns that each sum to 720 px; mix tall (180 to 240), medium (120 to 160) and thin (54 to 80) trays; 2 or 3 dark cards; 3 or 4 white trays; the rest tray grey with white inner cards.
  3. Widgets. Each W.name(t) returns HTML for time t. Pure function of t: no Math.random, no Date, no CSS transitions/animations. Reuse the helpers above.
  4. Make every state change continuous. For each widget check the cycle wrap (end state at l=P must equal start state at l=0 of the next cycle) and the pre-start state (t < offset). Fade out then fade in rather than popping.
  5. Review stills with snap.py, then render, then run smoothness.py. Fix any card with big spikes, re-render, re-check.
  6. Encode with encode.sh. Post the 60 fps file to X (X plays at most 60 fps); keep the 120 fps master.

Gotchas (all hit while building the reference)

Known limits

Verified