Paper cut-out (stop-motion paper) engine skill

Illustration · not a resultFrom the edenfunf/reelmimic README — shows the whole collection, not only this skill.source ↗
Make paper cut-out / stop-motion style animated videos (MP4) with code — handmade construction paper, cardstock, kraft, felt and tissue pieces with scissor-cut or torn edges and soft drop shadows, photographed on a table under a warm lamp.
by edenfunf·MIT license·★ 3,630 Stars on the repo·GitHub ↗
npx degit edenfunf/reelmimic/.claude/skills/paper-cutout#main ~/.claude/skills/paper-cutoutChecked ·commit main

Illustration · not a resultImages 1–4: From the edenfunf/reelmimic README — shows the whole collection, not only this skill.view on GitHub
Illustration · not a resultImage 5: From the edenfunf/reelmimic README — shows the whole collection, not only this skill.view on GitHub
Files of Paper cut-out (stop-motion paper) engine
Show the full text74 lines
Paper cut-out (stop-motion paper) engine
Every frame is a pure function of time t. studio.html loads src/kit.js (shared ReelMimic runtime: timeline,
motion helpers, camera, render hooks), src/cutout.js (this look: materials, cut/torn pieces, shadows, set dressing,
props, transitions, captions, the photographed finish), src/cast.js (the puppets) and one scene file per shot.
render.mjs drives headless Chrome for contact sheets, strips, crops and the final frames.
Workflow
- Scaffold —
bash .claude/skills/paper-cutout/scripts/new_project.sh <project-dir>(add--keep-demoto keep the example). Then readGUIDE.md(in this skill folder) in full, and look atdocs/demo-sheet.jpg,docs/expressions.jpg(13 expressions × both puppets) anddocs/poses.jpg. - Storyboard with reads (what the viewer must understand, when) — the same discipline as painted-animation's
ANIMATION_GUIDE.md(timing, one read at a time, anticipation → action → follow-through, transitions at every seam). - Build one IIFE file per shot in
src/scenes/, each ending withshots([[t0, fn]]); add its<script>tostudio.htmlaftercast.js. Setdurationinsrc/config.js. Start shots on multiples of 1/12 s. - Look —
node render.mjs --sheet=... --cols=4 --w=480 --out=out/check/a.jpg,--strip=a:bfor motion,--crop=x,y,w,h/--crop-at=for faces and hands. Open every image with Read. Fix, re-render. - Render —
node render.mjs --frames --workers=4 && node render.mjs --encode --out=out/video.mp4 [--audio=...].
API in one screen (all angles in DEGREES, sizes in px, time via sm(lt))
paperCam(lt, [[0,[cx,cy,zoom]], [2,[cx,cy,zoom]]], { stepped }) // camBegin with a (smooth | stepped) move
backdrop(PC.sky, { to }) layer(.5, () => ...) // screen-fixed sheet; parallax depth plane
hills(x,y,w,h,col,{seed,torn}) grass(x,y,w,h,col,seed) tree(x,y,s,{fruit,sway}) → {fruit:[[x,y]..]} bush flower
cloud(x,y,w,{hang:'string'|'stick'}) sun(x,y,r) house(x,y,s) bunting(x0,y0,x1,y1,n,cols) stripes confetti(lt,x,y,{t0})
piece(SH.xxx(...), { x,y,rot,sx,sy, col, mat, depth, torn, seed, alpha, jit }) // ONE cut-out; the base of everything
SH.ellipse/circle/rect/round/poly/smooth/capsule/blob/cloud/leaf/heart/star/drop/tri/band/zig/hills ; clipHalf ; xform
mat: 'paper' | 'card' | 'kraft' | 'felt' | 'tissue' | 'corr' | 'wood' brad(x,y,r) thread(pts) popUp(k,x,y,fn)
PROPS.apple|balloon|flower|star|gift|letter(x, y, ang, s)
// transitions — call AFTER camEnd(), in the INCOMING shot, k = 0→1 on stepped time:
trBook(t,k) trTear(t,k) trFlip(t,k) trSlide(t,k,{dir}) sheetIn(k) sheetOut(k)
captionTrack([[t0, t1, '文字', { style: 'label' | 'letters', y, size }]])
// puppets (cast.js) — (x, y) = ground point, u = size unit
pom(x, y, u, opts) lulu(x, y, u, opts) → joints { hand:[L,R], elbow, shoulder, foot, head, top, mouth }
opts: face, view:'front'|'3q', flip, look, blink, armL/armR:[shoulder,elbow], footL/footR:[dx,dy], sq, dy, lean, tilt,
holdL/holdR:'apple'|{prop,s,dy}|fn, jit, dry (measure only)
moods(lt, [[0,'sad'],[2,'surprised']]) walkCycle(lt,{stride,rate}) + walkSpeed() jumpPose(lt,t0,t1,h) emote(kind,x,y,s,age)
EXPR: neutral happy laugh sad cry angry surprised scared love sleepy wink determined shy
Rules that matter most
- Everything is paper. Draw only through
piece()/ the helpers (never rawfillRect/arcin scenes). Pick a material per object; torn edges for landscapes and labels, scissor edges for characters and props. - Depth = shadow. Background layers
depth.3–.8, set pieces ~1, puppets 1–1.3, things floating/held up 1.5–3. Parallax withlayer(k). Never fake 3D. - Stop-motion timing. Drive every puppet/prop pose with
sm(lt)(12 steps/s). Puppets jitter a hair per step by themselves; set pieces stay glued (jit: 0). Camera moves may be smooth (motion control) orstepped. - Replacement faces. Change expressions with
moods()(blink hides the swap), not by flippingfaceper frame. - Transitions from the kit of four (book fold, torn reveal, flip card, sheet slide) at every seam; the previous shot must NOT draw its own out-transition when the next shot uses one of them (they re-render it live).
- Legible at phone size: puppets big (pom u ≥ 55, lulu u ≥ 36 in a medium shot), captions ≥ 70 px.
- Captions only via
captionTrack(overlay, CJK font "Chiron GoRound TC" → "Zen Maru Gothic" → "Noto Sans TC").
Engine gotchas
- Pure function of
t: noMath.random(), no state between frames. Usehash/rnd/sway(key). piece()caches the cut outline by shape key +seed. Give each distinct piece its ownseed, or two identical shapes get identical wobble (fine for confetti, odd for a pair of trees).- The cut outline depends on the shape's size, so an animated size re-cuts every frame (edges shimmer). Scale with
sx/syor the camera instead of animating the shape's dimensions. prevFrame()renders the previous shot at the CURRENT time (it keeps moving under a transition) and swapsG; scenes must useG, not a captured context.- Props held by a flipped puppet are un-mirrored automatically (text/asymmetric props stay readable).
- Speed: ~5–30 ms draw per frame, ~130 ms/frame effective with 4 workers incl. JPEG encode at 1920×1080.
| 1 | |
| 2 | name paper-cutout |
| 3 | description Make paper cut-out / stop-motion style animated videos (MP4) with code — handmade construction paper, cardstock, kraft, felt and tissue pieces with scissor-cut or torn edges and soft drop shadows, photographed on a table under a warm lamp; jointed paper puppets with brass split-pin joints, moving in 12 fps steps. Canvas 2D rendered frame by frame in headless Chrome, encoded with ffmpeg. Use when a reference video or brief calls for paper craft, cut-paper, papercraft, stop-motion paper, kids' storybook collage, 剪紙 / 紙片 / 紙偶 / 定格動畫 looks, or when the video-clone router picks the paper-cutout style. |
| 4 | |
| 5 | |
| 6 | # Paper cut-out (stop-motion paper) engine |
| 7 | |
| 8 | Every frame is a pure function of time `t`. `studio.html` loads `src/kit.js` (shared ReelMimic runtime: timeline, |
| 9 | motion helpers, camera, render hooks), `src/cutout.js` (this look: materials, cut/torn pieces, shadows, set dressing, |
| 10 | props, transitions, captions, the photographed finish), `src/cast.js` (the puppets) and one scene file per shot. |
| 11 | `render.mjs` drives headless Chrome for contact sheets, strips, crops and the final frames. |
| 12 | |
| 13 | ## Workflow |
| 14 | |
| 15 | **Scaffold** — `bash .claude/skills/paper-cutout/scripts/new_project.sh <project-dir>` (add `--keep-demo` to keep |
| 16 | the example). Then read **`GUIDE.md`** (in this skill folder) in full, and look at `docs/demo-sheet.jpg`, |
| 17 | `docs/expressions.jpg` (13 expressions × both puppets) and `docs/poses.jpg`. |
| 18 | **Storyboard** with reads (what the viewer must understand, when) — the same discipline as painted-animation's |
| 19 | `ANIMATION_GUIDE.md` (timing, one read at a time, anticipation → action → follow-through, transitions at every seam). |
| 20 | **Build** one IIFE file per shot in `src/scenes/`, each ending with `shots([[t0, fn]])`; add its `<script>` to |
| 21 | `studio.html` after `cast.js`. Set `duration` in `src/config.js`. Start shots on multiples of 1/12 s. |
| 22 | **Look** — `node render.mjs --sheet=... --cols=4 --w=480 --out=out/check/a.jpg`, `--strip=a:b` for motion, |
| 23 | `--crop=x,y,w,h` / `--crop-at=` for faces and hands. Open every image with Read. Fix, re-render. |
| 24 | **Render** — `node render.mjs --frames --workers=4 && node render.mjs --encode --out=out/video.mp4 [--audio=...]`. |
| 25 | |
| 26 | ## API in one screen (all angles in DEGREES, sizes in px, time via `sm(lt)`) |
| 27 | |
| 28 | |
| 29 | paperCam(lt, [[0,[cx,cy,zoom]], [2,[cx,cy,zoom]]], { stepped }) // camBegin with a (smooth | stepped) move |
| 30 | backdrop(PC.sky, { to }) layer(.5, () => ...) // screen-fixed sheet; parallax depth plane |
| 31 | hills(x,y,w,h,col,{seed,torn}) grass(x,y,w,h,col,seed) tree(x,y,s,{fruit,sway}) → {fruit:[[x,y]..]} bush flower |
| 32 | cloud(x,y,w,{hang:'string'|'stick'}) sun(x,y,r) house(x,y,s) bunting(x0,y0,x1,y1,n,cols) stripes confetti(lt,x,y,{t0}) |
| 33 | piece(SH.xxx(...), { x,y,rot,sx,sy, col, mat, depth, torn, seed, alpha, jit }) // ONE cut-out; the base of everything |
| 34 | SH.ellipse/circle/rect/round/poly/smooth/capsule/blob/cloud/leaf/heart/star/drop/tri/band/zig/hills ; clipHalf ; xform |
| 35 | mat: 'paper' | 'card' | 'kraft' | 'felt' | 'tissue' | 'corr' | 'wood' brad(x,y,r) thread(pts) popUp(k,x,y,fn) |
| 36 | PROPS.apple|balloon|flower|star|gift|letter(x, y, ang, s) |
| 37 | // transitions — call AFTER camEnd(), in the INCOMING shot, k = 0→1 on stepped time: |
| 38 | trBook(t,k) trTear(t,k) trFlip(t,k) trSlide(t,k,{dir}) sheetIn(k) sheetOut(k) |
| 39 | captionTrack([[t0, t1, '文字', { style: 'label' | 'letters', y, size }]]) |
| 40 | |
| 41 | // puppets (cast.js) — (x, y) = ground point, u = size unit |
| 42 | pom(x, y, u, opts) lulu(x, y, u, opts) → joints { hand:[L,R], elbow, shoulder, foot, head, top, mouth } |
| 43 | opts: face, view:'front'|'3q', flip, look, blink, armL/armR:[shoulder,elbow], footL/footR:[dx,dy], sq, dy, lean, tilt, |
| 44 | holdL/holdR:'apple'|{prop,s,dy}|fn, jit, dry (measure only) |
| 45 | moods(lt, [[0,'sad'],[2,'surprised']]) walkCycle(lt,{stride,rate}) + walkSpeed() jumpPose(lt,t0,t1,h) emote(kind,x,y,s,age) |
| 46 | EXPR: neutral happy laugh sad cry angry surprised scared love sleepy wink determined shy |
| 47 | |
| 48 | |
| 49 | ## Rules that matter most |
| 50 | |
| 51 | **Everything is paper.** Draw only through `piece()` / the helpers (never raw `fillRect`/`arc` in scenes). Pick a |
| 52 | material per object; torn edges for landscapes and labels, scissor edges for characters and props. |
| 53 | **Depth = shadow.** Background layers `depth` .3–.8, set pieces ~1, puppets 1–1.3, things floating/held up 1.5–3. |
| 54 | Parallax with `layer(k)`. Never fake 3D. |
| 55 | **Stop-motion timing.** Drive every puppet/prop pose with `sm(lt)` (12 steps/s). Puppets jitter a hair per step by |
| 56 | themselves; set pieces stay glued (`jit: 0`). Camera moves may be smooth (motion control) or `stepped`. |
| 57 | **Replacement faces.** Change expressions with `moods()` (blink hides the swap), not by flipping `face` per frame. |
| 58 | **Transitions from the kit of four** (book fold, torn reveal, flip card, sheet slide) at every seam; the previous |
| 59 | shot must NOT draw its own out-transition when the next shot uses one of them (they re-render it live). |
| 60 | **Legible at phone size:** puppets big (pom u ≥ 55, lulu u ≥ 36 in a medium shot), captions ≥ 70 px. |
| 61 | Captions only via `captionTrack` (overlay, CJK font "Chiron GoRound TC" → "Zen Maru Gothic" → "Noto Sans TC"). |
| 62 | |
| 63 | ## Engine gotchas |
| 64 | |
| 65 | Pure function of `t`: no `Math.random()`, no state between frames. Use `hash`/`rnd`/`sway(key)`. |
| 66 | `piece()` caches the cut outline by shape key + `seed`. Give each distinct piece its own `seed`, or two identical |
| 67 | shapes get identical wobble (fine for confetti, odd for a pair of trees). |
| 68 | The cut outline depends on the shape's size, so an animated size re-cuts every frame (edges shimmer). Scale with |
| 69 | `sx/sy` or the camera instead of animating the shape's dimensions. |
| 70 | `prevFrame()` renders the previous shot at the CURRENT time (it keeps moving under a transition) and swaps `G`; |
| 71 | scenes must use `G`, not a captured context. |
| 72 | Props held by a flipped puppet are un-mirrored automatically (text/asymmetric props stay readable). |
| 73 | Speed: ~5–30 ms draw per frame, ~130 ms/frame effective with 4 workers incl. JPEG encode at 1920×1080. |
| 74 |
Discussion
Browse more free Claude skills.



