Free

Corner Drag

A handle rounds a card corner as a readout counts, the camera pulls back to a release card, and a cursor taps it inside a 3x3 grid.

Install

Install the "Corner Drag" skill into ~/.claude/skills/mb-corner-drag.
Download each file and save it under that folder:
https://skilllift.pages.dev/packs/mb-corner-drag/SKILL.md → SKILL.md
https://skilllift.pages.dev/packs/mb-corner-drag/references/style.md → references/style.md
https://skilllift.pages.dev/packs/mb-corner-drag/assets/engine.html → assets/engine.html
https://skilllift.pages.dev/packs/mb-corner-drag/assets/engine.js → assets/engine.js
https://skilllift.pages.dev/packs/mb-corner-drag/assets/fx-corner-drag.js → assets/fx-corner-drag.js
https://skilllift.pages.dev/packs/mb-corner-drag/assets/catalogue.json → assets/catalogue.json
https://skilllift.pages.dev/packs/mb-corner-drag/assets/library.json → assets/library.json
https://skilllift.pages.dev/packs/mb-corner-drag/assets/library.zh.json → assets/library.zh.json
https://skilllift.pages.dev/packs/mb-corner-drag/scripts/render.mjs → scripts/render.mjs
https://skilllift.pages.dev/packs/mb-corner-drag/scripts/check.mjs → scripts/check.mjs
https://skilllift.pages.dev/packs/mb-corner-drag/scripts/sheet.mjs → scripts/sheet.mjs
https://skilllift.pages.dev/packs/mb-corner-drag/scripts/shared.mjs → scripts/shared.mjs
Then read SKILL.md, tell me what it does, and remind me to start a new session.

Unpack it into ~/.claude/skills

Same skill, different brief

The left is the preview. The right was made by a separate agent from a different brief, using only the installed skill and without seeing the preview.

The preview
The preview
A new brief, made by a separate agent
A new brief, made by a separate agent

One test run on a fictional brief. Letterforms can differ on systems without the same fonts.

A handle drags a card corner round while a readout counts to a value you set. The camera pulls back to a release card, which settles in the centre of a 3x3 feature grid as a cursor taps its button. Use it for a design release or a product update. Renders one 1920x1080 30 fps H.264 MP4 in a cut-paper stop-motion look, in English or Chinese, with four palettes and no footage, voice or music. Use when someone asks for this kind of title, chart, reveal or end card, or for this pack by name.

What it must pass before delivery

  • check.mjs on the folder exits 0. Quote its last line.
  • You viewed real frames from the final MP4 at several points.
  • Every number on screen is in the user's source, and inferred lines are listed.
  • One language per version.
  • Fictional material is labelled through label.
  • No footage, voice or music was generated.
Read the full SKILL.md
---
name: mb-corner-drag
description: A handle drags a card corner round while a readout counts to a value you set. The camera pulls back to a release card, which settles in the centre of a 3x3 feature grid as a cursor taps its button. Use it for a design release or a product update. Renders one 1920x1080 30 fps H.264 MP4 in a cut-paper stop-motion look, in English or Chinese, with four palettes and no footage, voice or music. Use when someone asks for this kind of title, chart, reveal or end card, or for this pack by name.
version: 1.0.0
---

# Corner Drag

A handle drags a card corner round while a readout counts to a value you set. The camera pulls back to a release card, which settles in the centre of a 3x3 feature grid as a cursor taps its button. Use it for a design release or a product update.

Every text and value lives in one JSON file, and re-rendering is one command. The look lives in `assets/`. Your job is to fill the fields, never to restyle. Exact tokens and timings are in `references/style.md`.

## Inputs to ask for

Ask once, in one message, for what the brief does not give:

1. Name, the headline feature, button, readout label, a design value, up to 8 more features and up to 9 pictures.
2. The user's own text or facts. Paste them unchanged into `"source"`.
3. Language: `en` or `zh`. One language per version; for both, make two JSON files.
4. Palette: `vermilion`, `cobalt`, `pine` or `apricot`. If nobody says, use apricot.

**No answer comes:** proceed, and list your assumptions at the top of your reply.

## Steps

1. Write `clip/beats.json` in a new folder. `assets/library.json` and `assets/library.zh.json` are valid filled examples: copy the shape, not the fictional words.
2. Validate: `node <skill>/scripts/check.mjs clip/beats.json`. Fix everything it reports.
3. Look before rendering: `node <skill>/scripts/sheet.mjs clip/beats.json clip/sheet.png --at 0.99`, then view the image. Try `--at 0.5` as well.
4. Render: `node <skill>/scripts/render.mjs clip/beats.json --out clip --flat`.
5. Check what a viewer sees: `node <skill>/scripts/check.mjs clip`. Fix the JSON, re-render, and check again until it passes.
6. Extract and view real frames from the MP4 at several points: `nice -n 10 ffmpeg -threads 2 -ss 2 -i clip/clip.mp4 -frames:v 1 f.png`. The checker cannot judge whether it looks right. You can.

`--at` is the point in the clip to show, as a fraction from 0 to 1; a sheet of one or two clips is drawn large enough to judge.

Options: `--only clip --frame 120` re-renders one frame to `clip/frames/clip-120.png`. `--stills 0.3,0.6,1` writes stills to `clip/frames/`. `--scale 0.5` makes a draft at `clip/draft/clip.mp4`, even with `--flat`, and the checker refuses drafts. Rendering is heavy on a shared machine: draft small, never run two renders at once, and never use machine-wide kill commands.

`check.mjs` runs from wherever the skill is installed. Give it a JSON file, a folder (with `--beats path/to/beats.json` if the JSON is elsewhere) or any MP4. To run every check on a renamed MP4, add `--beats`. `check.mjs --test` is a short self test. It catches: overflow, a missing design label, stranded words, low contrast, frozen clips, wrong size or codec, and placeholders.

## beats.json

```json
{
 "lang": "en",
 "palette": "apricot",
 "label": "Fictional example",
 "source": "Fictional example: a bakery tool called Crumb.",
 "beats": [
  {
   "id": "clip",
   "job": "show",
   "effect": "corner-drag",
   "seconds": 7.7,
   "slots": {
    "name": "Crumb, new release",
    "feature": "Rounded recipe cards",
    "button": "Try it",
    "readoutLabel": "Corner radius (design)",
    "radius": 24,
    "features": [
     {
      "text": "Morning orders"
     },
     {
      "text": "Oven timers"
     },
     {
      "text": "Daily counts"
     },
     {
      "text": "Flour stock"
     },
     {
      "text": "Delivery slots"
     },
     {
      "text": "Shared recipes"
     },
     {
      "text": "Shift notes"
     },
     {
      "text": "Sunday menu"
     }
    ]
   }
  }
 ]
}
```

| Field | Meaning | Limit | Thin brief |
|---|---|---|---|
| `lang` | Language of all text on screen | `en` or `zh` | Use the language of the source |
| `palette` | The one colour setting | One of the four | apricot |
| `label` | Small tag on the clip | 30 characters, optional | Fictional example for samples, or 虚构示例. Leave it out for real work |
| `source` | The user's text, unchanged. Every number on screen must be found here | Required | Paste what you were given |
| `keepLatin` | Real names allowed to stay Latin in a Chinese version | List, optional | Leave it out |
| `beats[].id` | Clip file name | Letters, digits, hyphen | clip |
| `beats[].job` | Keep as shown | `show` | `show` |
| `beats[].effect` | Keep as shown | `corner-drag` | `corner-drag` |
| `beats[].seconds` | Clip length | 7 to 8.5 | 7.7 |

## Every field in slots

| Field | Meaning | Limit (characters) | Thin brief |
|---|---|---|---|
| `name` | Person, product or series name. | 24, required | Use the name in the brief. No name: use the script's title. |
| `feature` | The new feature this release is about, shown large in a band that stays to the end. | 34, optional | Use the feature the brief names. No single feature: leave it out. |
| `button` | The button on the release card. | 14, required | Use the action in the brief, or Try it, or 试试看 in Chinese. |
| `readoutLabel` | What the readout measures. Say it is a design value, such as Corner radius (design). | 26, required | Use Corner radius (design), or 圆角(设计值) in Chinese. |
| `radius` | The corner radius the readout counts to. This is a design value the user sets, not a measured fact, and the only figure here that needs no source. Drawn on the card at twice the value, up to half the card height, so the change reads on screen. | design value, required | Use 24. |
| `unit` | Unit of the readout. Default px. | 4, optional | Leave it out. |
| `features.text` | One feature in the grid around the card. | 26, required | Name only features the user listed; leave cells picture-only otherwise. |
| `features` | List (0 to 8 items) | | |
| `pictures` | List of {"src":"file.png"}, PNG or JPG, relative to the JSON. Reused in turn when there are fewer than the effect shows. | up to 9, optional | Leave it out: code-drawn abstract pictures in the palette stand in. Use real pictures whenever the user has them. |

## Rules

**The readout is a design value** the user sets, not a measurement, and it is the only number here that needs no source. Say so in `readoutLabel`, as in Corner radius (design). The corner is drawn at twice the value, up to half the card height, so the change reads on screen. Default: 24. **Pictures:** PNG or JPG, reused in turn. With none, code-drawn abstract pictures stand in. **The feature the brief is about** goes in `feature`: a large band that stays to the end. **`features`** fill the neighbouring cells in reading order, skipping the centre: name only features the user listed; other cells show a picture alone. **Pictures:** picture 1 is on the centre card, the rest fill the neighbours in the same order. They are cropped around their centre to about 2:1, so keep the subject in the middle, or set `"focus"` on a picture. **The button:** the brief gave none? Use Try it, and list it as inferred.

**Generate no invented numbers.** Every figure must be in `source`, and the checker fails any that is not. **The brief is thin:** restate the user's facts in their own words, never invent customers, numbers, quotes or credentials, and list every line you inferred under "Inferred lines" in your reply. **Nobody can answer:** proceed and list your assumptions. **Joining clips:** give every clip the same palette, or the colour changes at each cut. **A different length is wanted:** change `seconds` within the range. **No real link:** do not show an address. **Line breaks:** words are kept whole; if a break reads badly, put `\n` in the text.

## Deliverables

`clip/beats.json`, the one file to edit, and `clip/clip.mp4`: 1920x1080, 30 fps, H.264, yuv420p, silent. Add a note with assumptions, inferred lines and the re-render command.

## Render time

Measured on a 16 GB Mac with other work running, at low priority and full size: 6 s for this 7.7-second clip with its default material.

## Before delivering

- [ ] `check.mjs` on the folder exits 0. Quote its last line.
- [ ] You viewed real frames from the final MP4 at several points.
- [ ] Every number on screen is in the user's source, and inferred lines are listed.
- [ ] One language per version.
- [ ] Fictional material is labelled through `label`.
- [ ] No footage, voice or music was generated.

## When not to use it

The readout would be taken for a measured result such as a benchmark. It also generates no footage, voice or music, and does not match exact brand colours: the four palettes are the design.

## Requirements

- Node 18 or later, and ffmpeg with ffprobe on the PATH or in /opt/homebrew/bin.
- Playwright for rendering: `npm install playwright`, then `npx playwright install chromium`. If it is installed elsewhere, set `NODE_PATH` to that `node_modules`. `check.mjs` needs no packages.
- Fonts are system stacks. Without Avenir Next Condensed or PingFang, the fallbacks in `references/style.md` apply and the text is re-fitted. Run the check there.

Used in these workflows

Similar skills