HTML Demo, a skill for Claude Code, Codex and Cursor

A skill for Claude Code, Codex and Cursor: show your app working on a web page, drawn live in HTML instead of recorded. Every frame is a function of one clock at a fixed 1200 × 750 stage, so it stays sharp, loops, scrubs and stands still for reduced motion.

Install it

Claude Code:

curl -fsSL https://fil.design/skills/html-demo.zip -o /tmp/html-demo.zip && unzip -o /tmp/html-demo.zip -d ~/.claude/skills

Codex:

curl -fsSL https://fil.design/skills/html-demo.zip -o /tmp/html-demo.zip && unzip -o /tmp/html-demo.zip -d ~/.codex/skills

GitHub:

npx degit MengTo/Skills/agent-skills/web-design/html-demo ~/.claude/skills/html-demo

Or tell your agent:

Install the HTML Demo skill from https://fil.design/skills/html-demo: unzip https://fil.design/skills/html-demo.zip into your skills folder (~/.claude/skills for Claude Code, ~/.codex/skills for Codex), then read its SKILL.md.

The skill’s folder: https://fil.design/skills/html-demo.zip (zip), GitHub, and each file:

SKILL.md

React · DOM/CSS (a vanilla twin is in scripts/step-window.js). No video, canvas, WebGL or screen recording.

It looks recorded, but it's HTML. Each frame is computed, so it stays sharp at any size and pixel density, its words are real text, and it can loop, jump to a step, scrub, freeze for a screenshot and stand still for reduced motion, all from the same code.

Boundary. Use browser-video-recording when you need a real recording of a real browser as an MP4. Use website-to-motion-design when a page becomes a fixed-length film. Use this skill when the deliverable is a live window on a web page that shows an app working.

Provenance. Extracted from fil.design/download (October 2026). There, Fil for Mac's hero window runs four workflows end to end. Eight chapter windows each show one feature step by step: import a URL → Design → the agent; prompt → inpaint → outpaint → layers → angle; record → motion design → edit → transcript; sites; the browser. One lead built the engine and the hero, and one helper agent built each chapter window from a shared brief.

The mechanism

Every frame of the app is a pure function of one number. A step's View({ t }) draws everything inside the window at a fixed 1200 × 750 design size from t alone, where t is seconds into the step. One CSS scale (--k = width / 1200) then fits that stage to the window.

If you take that away, the demo breaks. When state lives in setTimeout chains, animation callbacks or effects, you can't jump to a step. It drifts after a hidden tab, screenshots differ between runs, and reduced motion catches it half-done. Designing responsively inside the window breaks it differently: text reflows at every width, the pointer misses its targets, and phones show a different app.

import StepWindow, { Cursor, ease, typed, glide } from './scripts/StepWindow.jsx';

const ASK = 'The Ashen One, a knight in blackened armour resting on his sword';
function Prompt({ t }) {                           // t: 0 → dur, nothing else
  const text = typed(ASK, t, 0.9, 2.6);            // typed between 0.9 s and 2.6 s
  const made = ease(t, 3.4, 5.2);                  // the picture draws in
  const c = glide(t, [[0, 0, 900, 600], [0.2, 0.8, 520, 330], [2.8, 3.3, 760, 470]], [0.85, 3.35]);
  return (<>
    <App prompt={text} reveal={made} />            {/* the app's real markup at 1200 × 750 */}
    <Cursor {...c} />                              {/* last, over everything */}
  </>);
}
export const STEPS = [
  { id: 'prompt', name: 'Prompt', dur: 6, View: Prompt },
  // 3–5 steps, 4–8 s each
];
<StepWindow steps={STEPS} tabs={['The Ashen One']} label="Make a picture" />

scripts/StepWindow.jsx provides the window, its clock (useStepClock), the scaling, the steps bar, Cursor, and the helpers ease, typed, cue, held and glide. references/contract.md has the full API, and references/film.md covers the hero variant (many flows, a camera, beats).

Workflow

  1. Pick one example and use it everywhere. Choose one believable subject you own, such as a product, a character or a site (Fil used The Ashen One and Ashgrove), and run every window on it. Never show a client's, partner's or real person's canvas. A window full of real names you can't show is unusable, and different subjects in every window read as scraps.
  2. Gather references of the real UI before drawing anything. Use screenshots, the docs' recordings (pull frames with ffmpeg -ss 2 -i in.mp4 -frames:v 1 out.png) and the app's source code. List the references for each step. Copy every label, menu item, toast and cost from the source with grep, never from memory. If a viewer who knows the app spots one invented word ("Processing…"), they stop believing the rest.
  3. Script the steps on paper. A window gets 3–5 steps of 4–8 s, 18–30 s in all. Each step gets a beat to see the start (0.3–1 s), the action, the result appearing the way the app shows it (a working state, then the result), and at least 1 s holding on the result. Name the steps as the bar shows them, in 1–3 words ("Paste a link", "Inpaint").
  4. Write each step as named times, then derive state from t. Use constants like TYPE_FROM = 1.9, SEND_AT = 4.3, DONE_AT = 6.9, then comparisons (t >= SEND_AT), ease(t, a, b) for anything that moves, typed() for typing and cue() for lists of changes. Keep times as constants at the top so the choreography reads like a script and can be retimed in one place.
  5. Draw the app at 1200 × 750 from those states. Reuse the real app's markup and CSS where you can. Otherwise copy sizes from its stylesheet: rail width, top bar height, radii, type sizes. Prefix every class and @keyframes with the window's own prefix (ximp-, xpic-) and grep to confirm nobody else uses it.
  6. Choreograph the pointer like a person. It arrives 0.3–0.6 s before a click, presses for 0.15–0.2 s, and glides 0.5–0.9 s between targets. Between actions it rests out of the way, and it fades when the viewer should look at the result. It must aim at real elements (data-aim measured from the DOM, or coordinates checked in a screenshot).
  7. Shoot, compare, fix: at least three rounds. Take frames every 0.5–1 s through each step (scripts/shoot.mjs) and put them side by side with the references. Ask: would someone who uses this app believe it's a recording? Fix structure (layout, sizes, wording) before polish (motion).
  8. Check the edges. Look at the reduced-motion end frames, phone widths (390 px and 340 px), a hidden tab coming back, and the console.

Rules, each with the failure it prevents

Numbers that shipped

whatvalue
stage1200 × 750, --k = clientWidth / 1200
steps3–5 per window, 4–8 s each, 18–30 s per window
hold on the result≥ 1 s
typing~50 characters/s (90 characters in 1.8 s); a person reads it, nobody waits for it
pointer glide0.5–0.9 s, cubic-bezier(0.55, 0, 0.25, 1); arrive 0.3–0.6 s early
click0.15–0.2 s pressed; arrow scale 1.15 → 0.98
step fade-in0.45 s, cubic-bezier(0.2, 0.7, 0.1, 1)
scene crossfade300 ms lag before the next scene draws
clocktick 40 ms (windows) or 50 ms (film); dt ≤ 0.25 s; on-screen threshold 0.3 (0.15 hero)
threads1.6 px at 0.8 opacity in the source node's colour; live: dashes 6 7 marching 0.7 s; quiet (brief, skill): 0.14
cameraup to 1.2× on the step being made plus what feeds it; whole canvas 0.86–1.08×; glide 1.3 s cubic-bezier(0.45, 0, 0.2, 1)
time shown18 s of app time for every demo second

Many windows at once

Build the engine and one window yourself first. That window is the style guide. Then give each helper one window and a shared brief (references/brief.md). Each helper creates only its two files with its own prefix, previews through one shared dev server (scripts/preview-plugin.mjs, at /preview?entry=), works in its own scratch folder and runs no git. Each one shoots, critiques and fixes for at least three rounds, then reports its steps, screenshot paths and "what still falls short".

Read those reports: the gaps they list are the next round. Helpers stop at usage limits. Resume them with a message rather than starting new ones, because their work so far is kept.

Verify

Files