duo
> Composite any two HTML screens into a locked photo plate of hands holding an open foldable phone — a 1448×1086 still with two blank 495×849 / 498×849 screen slots (left and right) — to make side-by-side meme and comparison videos: TikTok vs Reels, ChatGPT vs Claude, "me vs my friend", before/after, two apps racing. Trigger on: "foldable phone meme", "put X on the left screen and Y on the right", "two-screen comparison video", "the dual-screen phone meme", or any request that names two apps/feeds/chats to show at once. The plate, geometry, and camera are finished — the agent only authors what plays INSIDE the two screens and renders.
1How It Works & Mechanism
Core trick: The setup is not built from scratch, but utilizes an existing photo. plate.png is the unfolded phone (including hands, bezels, hinge); the skill strictly prohibits redrawing, CSS modeling, or swapping the image; if the image is missing, halt and reinstall instead of inventing a phone.
`assets/plate.png` IS the device. Never recreate, redraw, or CSS-model the foldable, its bezel, the hinge, or the hands, and never substitute a different image.
The rig is a locked block: The two .screen rectangles, their respective border radii, and the hinge divider were measured pixel-by-pixel from that photo; the agent is only permitted to modify the content inside each .content and the single shared timeline.
/* ===== LOCKED RIG — do not edit this block. Geometry is measured from the plate. ===== */
Masking via geometry rather than segmentation: Luminance segmentation + morphological closing was previously tried for mask-image, but it flattened the 12 px hinge-side corner radius and swallowed anti-aliasing pixels of the hinge divider. It was switched to direct CSS rounded rectangles after numerically measuring edges and radii.
Lesson: for device-screen replacement, measure edges and corners numerically and build the mask from geometry. Never ship a morphology-cleaned segmentation as a mask.
Timeline discipline serves seekability: Only one shared GSAP timeline is allowed for the entire piece (tweens added to tl at markers); creating a second timeline or registering a second window.__timelines key is forbidden, as are repeat:-1, Math.random, and clocks.
One timeline. Add your tweens to `tl` inside the marked hook; do not create a second `gsap.timeline` or register another `window.__timelines` key.
The scaffolding is intentionally minimal: build.mjs has no network access, no third-party dependencies, writes strictly inside --out, checks lstat before every write, and refuses to write through symlinks; it copies the plate, screen-spec, and (optional) real icon symbol sheets into the project, hardcoding frame specs to 1448×1086@30fps.
if (st.isSymbolicLink()) throw new Error(`refusing to write through symlink: ${p}`);
"Looking real" is guaranteed by strict sourcing discipline: If the user asks for TikTok/Instagram, real icons and layout metrics must be extracted from the mobile web version, paired with genuine posts and authentic account counters; inventing handles is only permitted for fictional/parody screens explicitly requested by the user.
Real content must be real: if the user asks for TikTok or Instagram, pull the actual UI glyphs and layout metrics from the live site and use real posts (with their real usernames and counts), not invented ones.
The deliverable surface is intentionally narrowed: The entire piece is locked at 1448×1086 (native 4:3 of the base plate). Desired 16:9 or 1:1 aspect ratios can only be achieved via downstream cropping; altering rig dimensions to accommodate content is forbidden.
| Plate | 1448 × 1086 |
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| cli | HyperFrames CLI(npx hyperframes@latest;check / snapshot / render) |
| network | GSAP 3.14.2(合成页在预览/渲染时由浏览器加载;未随包分发) |
| cli | yt-dlp(取真实帖子的说明、计数与时长;登录态外可用) |
| network | Instagram Reels 直链下载(登出页面里的 scontent-*.cdninstagram.com mp4,用 Safari UA + instagram.com Referer 取;链接数小时内失效) |
| package | Node 18+(自带脚本无第三方依赖) |
| cli | 素材来源:用户自备或按上面配方自抓的片段、头像、字体(SKILL.md 把它们归为用户侧副作用,须来自有权使用的来源) |
| package | 随包的真实应用图标表(icons-tiktok.svg / icons-instagram.svg,从公开移动端 DOM 抠出的 <symbol> 集;含他人商标,声明为戏仿/评论用途) |
4Recommended Governance & Evidence
- scripts/: Execute local script or command: node scripts/build.mjs --out <dir> [--id …] [--duration 10] [--icons tiktok
- 网络端点: Declared external network access: registry.npmjs.org: HyperFrames CLI fetched via npx (SKILL.md notes mutable @latest, recommending version pinning)
- 本地工作目录: File I/O and artifact generation: build.mjs writes strictly within --out: index.html, hyperframes.json, assets/{plate.png,screen-s
5Second-Pass Verification
- [ok] build.mjs 是否真的无网络、无第三方依赖 — import 仅 { promises as fs } from 'node:fs'、path、fileURLToPath;无 fetch/http/https/dns/child_process。全部行为是读模板、字符串替换 __COMPOSITION_ID__/__DURATION__、写文件与拷贝资源。头注释亦声明 '// No network access. No dependencies beyond Node 18+.'
- [ok] 『不改 rig』是否只是文案(模板与 spec 是否一致) — 模板 #screen-left{left:223px;width:495px} / #screen-right{left:724px;width:498px}、.screen{top:105px;height:849px}、圆角 56px 12px 与 screen-spec.json 的 rect/outer_corner_radius_px/hinge_corner_radius_px 完全对应;build.mjs 也把可用视口打印成 '495x849' / '498x849'。
- [ok] feed-recipes.md 引用的图标 symbol 是否随包(避免抄了配方却没有图标) — grep 确认 assets/icons-instagram.svg 内含 id="ig-like-filled"(配方里 tl.set('#ig-heart-use', { attr: { href: '#ig-like-filled' } }, 2.7) 用到),另有 ig-like/ig-comment/ig-share/ig-home/ig-reels/ig-search/ig-camera/ig-verified 等 10 个 symbol;icons-tiktok.svg 含 tt-like/tt-comment/tt-bookmark/tt-share/tt-plus/tt-home/tt-discover/tt-inbox/tt-profile/tt-search/tt-add/tt-live/tt-verified/tt-note 共 14 个。
- [discrepancy] SKILL.md 的『Network and side effects (complete list)』是否真的完整 — 该清单只列 registry.npmjs.org 与 cdn.jsdelivr.net,把抓取归入『Anything YOU add to the screens … is your side effect』;但同目录的 references/sourcing-real-ui.md 给出了具体抓取路径(www.tiktok.com、instagram.com、scontent-*.cdninstagram.com)与 UA/Referer 做法,并附 ffmpeg 裁片命令。对只读 SKILL.md 的用户而言,执行期外发面被低估了(文档级出入,非代码行为不符)。
- [unlocatable] 『几何是从照片逐像素量出来的』能否在包内独立复核 — screen-spec.json 给出了量测结论(左右 rect、r=56/12、铰链 x719–723)与出处推文,但两个屏幕已被抹平、原始推文照片不在包内,且本次为只读侦查不联网取原图,故这些数值无法独立复算;references/screen-geometry.md 提供的是重测方法而非原始数据。
- [ok] 状态栏与铰链的还原是否有据可依 — screen-spec.json 的 hinge 字段(x 719 / w 5 / rgb(130,133,142))与 status_bar(左 9:41@x44,y18;右 wifi_battery_right_inset 40)给出像素级依据;feed-recipes.md 的忠实度清单要求复现该分置,SKILL.md 亦在 Flow 第 3 步复述。
- [ok] 底图与预览图的引用是否都在包内 — SKILL.md 正文  与 build.mjs 拷贝的 assets/plate.png、assets/screen-spec.json 均在包内;plate-preview.jpg 仅作预览,不参与渲染。
6Verdict & Scenarios
d33100badc2a58ee…