---
name: ui-motion-promo
description: >-
  Builds polished product-UI motion videos in code with Remotion, in the premium launch-ad style where a real app interface plays out on screen with a smooth camera, a realistic cursor, drag and drop, typing and results arriving. Use when someone asks for a launch video, promo clip, product demo animation, "motion video of our UI", an ad like Higgsfield, Linear or Apple, a feature reveal, or wants to show any app flow (uploading, sending a chat prompt, a CRM board, a checkout, a dashboard) as video, even if they never say "Remotion" or "motion design".
license: MIT
compatibility: >-
  Coding agents only (Claude Code, Codex, Cursor, Gemini CLI, VS Code): it installs Remotion with npm and renders with headless Chromium, which needs Node 18+, ffmpeg, Python 3 with numpy (for the measuring and sound scripts), network access and a real machine. Claude web and ChatGPT web cannot run it.
metadata:
  author: Icarus (withicarus.com)
  version: "1.5"
---

# UI motion promo videos

Turns any app flow into a premium motion video: the app's real interface rebuilt in React, animated frame by frame with Remotion, rendered to MP4. The bar is a paid launch ad, not a screen recording. Runs in coding agents only, because it installs and renders on a real machine.

`assets/template/` is a working two-scene video (photos dropped into an upload card, then a prompt sent in a chat that runs tools and returns photos). Its scenes are examples; its cursor, motion blur, easing, camera, transition and checking loop work for any app.

## Workflow

Copy this checklist into your reply and tick it off as you go:

```
Motion video progress:
- [ ] 1. Gather inputs (look first, then one message for what's missing)
- [ ] 2. Study the reference video
- [ ] 3. Set up the project from the template
- [ ] 4. Build each scene's UI at full size, matching the real product
- [ ] 5. Animate: beats first, then cursor, then camera
- [ ] 6. Check frames, fix, check again (until the list in step 6 passes)
- [ ] 7. Add sound (user picks by ear), render 4K, report
- [ ] 8. Take feedback version by version
```

### 1. Gather inputs

The video can only look like the real product if it is built from the real product. Guessed screens, a made-up logo or stock photos are what make these videos look generated.

**Look before you ask.** Search the codebase and the conversation for each item below. Take what you find; ask only for the rest.

| Input | Why it matters | Where to look first | If they don't have it |
|---|---|---|---|
| The flow: which screens, in what order, what the cursor does | Decides every scene | Their message | Ask; nothing can be built without it |
| The product's parts: theme tokens, icons, brand pieces (loader, placeholder, buttons, cards), state words | The video's new screens are built from them so they still look like the product | Global CSS, Tailwind config, icon adapter, components, Storybook | Read the code; if there is none, ask for screenshots |
| Theme: light or dark, font, brand colour | The wrong theme reads as a different app | Global CSS, Tailwind config, design docs | Light, Inter, the logo's main colour |
| Logo file | A generic icon in its place is noticed first | `public/`, brand folders, site config | Ask; never stand one in |
| Real images and data shown in the UI (photos, products, names, numbers) | Placeholders make it a template | Their CDN, seed data, the conversation | Ask; numbers must be real, not invented |
| A reference video they like | Sets pace, framing and cut style | Links in their message | The defaults in `references/polish-rules.md` |
| Length and format (16:9, 9:16, 1:1) | Sets composition size and timing | Their message | 15-20s, 16:9 |
| Exact text for every line on screen | Avoids rewrites | Repo copy, their message | Draft it plain and short, then show it |
| Music or sound | You cannot hear, so any sound you choose is a guess | Their message | Effects from the hosted library (`references/sfx.md`), only on visible events; the user picks by ear from an audition video. No music unless asked |
| Another company's app on screen (Claude, ChatGPT, Slack...) or an "A × B" line | Reads as a partnership | Their message | Show it plainly, and flag that they should get permission before publishing |

Then send **one** message listing only what is still missing, numbered, each with a short reason and the default you will use if they say "you decide". For example:

```
To make this look like your real app, I need a few things:
1. Screenshots of the deals board and the deal panel (I'll match the layout exactly).
2. Your logo file (SVG or PNG).
3. 6-8 real deal names and amounts to show (or I'll use anonymised ones from your seed data).
4. A reference video you like, if any (otherwise a calm, Apple-style pace).
Theme: I found light mode and Inter in your Tailwind config, so I'll use those.
```

Keep it to five questions or fewer. One message of questions costs less than a render nobody wants; ten questions before any work gets the skill dropped.

### 2. Study the reference video

If there is one, download it and look at it frame by frame:

```bash
ffmpeg -i ref.mp4 -vf "fps=6,scale=480:-1,tile=6x4" ref-sheet.png                              # overview
ffmpeg -i ref.mp4 -filter:v "select='gt(scene,0.2)',showinfo" -f null - 2>&1 | grep pts_time    # cut times
```

Note how fast things enter, how long they hold, how the cursor moves, how the camera moves between screens, and how much of the frame the UI fills. Measure it so you have numbers to match rather than an impression:

```bash
<this-skill>/scripts/measure-rhythm.py ref.mp4 --from <start> --to <end>   # how much of the time something moves
<this-skill>/scripts/measure-camera.py ref.mp4 --from <start> --to <end>   # each camera move, speed per frame
```

Copy what it does, including what it does not do (for example, it may never zoom). Add nothing it does not have: an extra sidebar, title card or end card reads as filler and gets removed. When the user names one reference, use only that one; mixing in a second ad's moves was rejected.

### 3. Set up the project

```bash
cp -R <this-skill>/assets/template my-video && cd my-video && npm install
```

Put every image, the logo and every line of on-screen copy in `src/shots.ts`. Theme, font, easing and the 60 fps clock live in `src/light.tsx`. Sounds live in `src/sound.ts`. `src/Promo.tsx` holds the one ordered list of scenes and the camera that travels across them.

The scenes are laid out on a 1920x1080 canvas (their centre is 960, 540). For 9:16 or 1:1, add a composition of that size in `src/Root.tsx` and lay each scene out for it; scaling a landscape layout into a tall frame leaves it tiny.

### 4. Build each scene's UI

**Design new screens for the video, made from the product's own parts.** The video is not a copy of a page; real pages carry navigation, forms and fine print that slow a video down. Design simple, video-only screens that tell the flow (two drop boxes, one result card, one line of text), and build them from the product's real parts so they still look like the product:

- its theme tokens: colours, radius, borders, font and weights
- its icon set and exact glyphs (through its own icon adapter if it has one)
- its brand pieces: the loading indicator, placeholder or loading effect, button and card styles
- its own words for states and actions

Find each part in the repo and recreate it as plain React in the video project (Remotion cannot import the app itself: routing, data and providers do not exist in a render). For example, a launch video for Icarus's Wings feature did not rebuild the /wings page: it used two new drop cards, a new result card and a typed address, built from Icarus's loading mark (`Orb.tsx`, a 3x3 dot wave), its loading glow (`.aurora-placeholder` in `globals.css`), its Hugeicons glyphs and the page's own stage words. Generic stand-ins (a spinner, another icon set, invented copy) are what make a video look like a template.

Build at the size it will be seen (text about 19-27px on a 1920x1080 canvas, the UI filling about 90% of the frame height). Reuse the template's motion pieces (drag, cursor, camera, sound cues), not its screens.

Read `references/polish-rules.md` before this step. It lists every detail that made earlier versions look cheap, each with its fix and its reason (sizes, drag cards, no shadows, the camera, typing, loading and results). Read `references/chat-ui.md` when a scene shows a chat app.

### 5. Animate

Beats first (what moves when, and how long it holds), then the cursor (locked to what it drags), then the camera. The camera never zooms: the screens sit on one page and one camera moves across it (`Promo.tsx`), so the whole film is one continuous shot instead of clips joined by fades.

Three judgments decide whether it feels produced or generated (details and reasons in `references/polish-rules.md`, "Pacing" and "Restraint"):

- **Pace by what the viewer must read.** The setup (what is dragged in, what is typed) goes slower; the payoff (the work running, results arriving) goes faster. Too fast and nothing registers; too slow and it drags. Change pace by adding or cutting holds, never by speeding the motion itself.
- **Motion comes from a cause.** Animate what is actually moving (a dragged item, a sent message, a result arriving) and let it travel from where it really comes from. Extra flourishes on things that just appear make them look like they came from nowhere.
- **Many items move as one gesture, in one direction**, starting from where they were let go.
- **A carried item moves on the drag curve with centred blur.** One continuous move that builds, peaks and glides into the drop; blur only on its fastest frames, centred so it never trails the cursor.
- **Each motion finishes before the next starts.** The camera lands, the drag runs, the item lands and shows its ×, a short hold, then the camera moves on. Overlaps read as out of sync.
- **Flow, not start-stop.** Long moves on Astra's camera curve, a still camera between moves with the content doing the moving, no state longer than about 2s, and a still final frame. Measure against the reference.

### 6. Check frames, fix, check again

Run the bundled script from the project folder. It renders the frames you name into one sheet:

```bash
<this-skill>/scripts/check-frames.sh Promo 20 60 120 200 300 400
```

Look at the sheet, and at the frames around every change (a drop, a click, a send, a cut). Check each against this list:

- Text is crisp and the UI fills most of the frame height.
- The cursor sits on what it holds; nothing is clipped at an edge.
- Spacing is even; icons share one size and stroke; nothing shifts when a button changes state.
- Products and faces are fully visible in every image tile.
- No two scenes overlap; no frame where the layout jumps.
- Nothing on screen that the user did not ask for.
- No zoom, no shadows, no bounce; each motion finishes before the next starts.
- The motion flows: `scripts/measure-rhythm.py` shows few long moves, no dead stretch, and a moving share close to the reference's.

Fix what fails and run the script again. Move on only when the list passes.

### 7. Sound, render and report

Read `references/sfx.md`. Put sounds only on events the viewer sees (a key typed, an item landing, the camera moving, a result appearing), from the hosted library. Make an audition video when the user has not picked sounds yet. Then:

```bash
npx remotion render src/index.ts Promo out/promo-v1-4k.mp4 --scale=2
```

`--scale=2` renders at 4K so small UI text stays sharp; the composition is 60 fps. Add `--muted` for a silent cut. Give every render a new version number and keep the source of each version, so any earlier one can be brought back exactly. Report what changed, which frames you checked, and what you have not watched end to end.

### 8. Take feedback version by version

Feedback on motion is usually a comparison ("the first one was better", "too fast now"). Work from versions, not from memory:

- **Name the version.** When feedback points at "the first one" or "earlier", ask which file, by its name, before changing anything. The wrong guess costs a full round.
- **Go back, don't pile on.** When a change is rejected, restore the version that was liked and change only the part named. Fixes stacked on a rejected version keep its problems.
- **Change one part at a time.** Keep the parts the user approved exactly as they were; say which parts you did not touch.
- **Swing less each round.** If one version was too slow and the next too fast, the answer is between them, not another extreme.

## Examples

<example>
User: "Make a launch video like this Higgsfield ad for our photo app: someone drags mood-board photos in, then asks Claude for a campaign and the photos come back. Light mode, our real UI."
Do: find the theme and logo in the repo. Ask in one message for the mood-board photos, the product photo and the result shots. Pull frames from the ad. Start from the template: the drop card for the mood board, the chat for Claude. Check frames, then render 4K.
</example>

<example>
User: "Animate our CRM: a deal gets dragged from Qualified to Won and the revenue number goes up. 10 seconds, vertical for LinkedIn."
Do: find the board component and theme in the repo. Ask only for the real deal names and amounts and the logo if they are not there. Add a 1080x1920 composition, build the board at full size, reuse the drop card's drag code (cursor locked to the card, speed blur, small tilt, settle) for the deal, and count the revenue up with ease-out. Check frames, render.
</example>

<example>
User: "Make a cool motion video for our launch."
Do: look in the repo for the product and its theme, then ask one message: which flow it should show, whether there's a reference ad they like, which real images or data to use, and how long. Building from "cool" alone produces a generic video that gets rebuilt.
</example>

## Common mistakes

| Mistake | Fix |
|---|---|
| UI built tiny, then zoomed 1.5-2x | Build at full size; one fixed zoom for the whole film |
| Cursor and dragged item drift apart | Compute the cursor from the item's grip point after its rotation and scale |
| Blur that trails behind the object (`CameraMotionBlur`, or a Gaussian from last frame's movement) | Reads as lag; use the centred `MotionBlur` in `src/light.tsx`, only above a speed threshold |
| Ease-out on a dragged item | It jumps then stalls; use the drag curve and check per-frame movement |
| Zooming or fading between screens | One page, one camera: pan to the next screen on the measured curve, blurred along the move |
| Shadows on cards, photos or the cursor | Read as AI-generated; use a hairline or nothing |
| A click or whoosh with nothing on screen causing it | Sound only visible events; an off-screen grab click was heard as random clicking |
| The camera moves while a drag is still landing | Let each motion finish: land, show the ×, hold, then move |
| A button swaps for a different-sized one | One fixed slot; only its look changes |
| Faces or products cropped in tiles | Set `objectPosition` per image after looking at each full photo |
| Asking what the repo already answers | Look first; ask only for what is missing |
| Guessing which earlier version the user means | Ask for the file name, then restore that version and change only the named part |
| Picking sound or music yourself | Offer an audition video; the user chooses by ear |
| On-screen copy edited inside a scene file | Keep it in `src/shots.ts`, so the next product swaps one file |
| JSX inside a `.ts` file | Esbuild fails with `Expected ">"`; use `.tsx` |

## Files

| Path | What it is |
|---|---|
| `README.md` | For people: install, run it from an app repo, example asks, how to give feedback |
| `references/polish-rules.md` | Every polish rule with its reason: size, type, icons, cursor, drag, blur, easing, camera, transitions, chat flows, images |
| `references/chat-ui.md` | Measurements and colours for a Claude-style light chat |
| `references/sfx.md` | Sound rules, starter picks, placing a sound on its frame, typing keys, auditions |
| `references/sfx-library.md` | All 250 hosted effects with CDN links and lengths |
| `references/sources.md` | The research behind the rules |
| `scripts/check-frames.sh` | Renders chosen frames into one contact sheet |
| `scripts/measure-rhythm.py` | Measures how much moves each frame and lists moves and holds, to compare a render with a reference (ffmpeg, numpy) |
| `scripts/measure-camera.py` | Measures each camera move's per-frame speed in a reference, to copy its curve |
| `scripts/prep-sfx.py` | Downloads a hosted effect, normalises it, prints its lead in frames; `--keys N` cuts single keystrokes from a typing take |
| `assets/template/` | The working Remotion project to start from |
| `evals/evals.json` | Three test requests with the behaviour expected, for checking the skill after changes |

---

Page: https://withicarus.com/skills/ui-motion-promo
Download: https://onai.b-cdn.net/skills/ui-motion-promo/v6-d22ed91f/ui-motion-promo.zip
Install guide for AI apps: https://withicarus.com/skills/ui-motion-promo/llms-install.md
This skill makes photos and videos with Icarus. Connect Icarus to your AI app: https://withicarus.com/mcp
