tutorial-video
Skill1 starsChanged 49 days agoMITView on GitHub
Produce narrated product tutorial videos automatically — script, voice-over (ElevenLabs, OpenAI, Higgsfield or macOS say), a real recording driven by Playwright (web app, browser-extension popup) or the iOS simulator (mobile app), cursor/tap highlights, captions, ducked music, title and end cards, 1080p mp4. Use when the user wants a tutorial, demo, walkthrough, onboarding or sales video of an app, or wants to re-record one in another language.
- Reads credentials
2 other projects use a version of this.
Adopted by
Most recently taken up by
- speechlab0210/tutorial-video-skilltheir own version, first seen Oct 3, 2026
- Neelshah1810/Claude-Video-Skillstheir own version, first seen Oct 2, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- speechlab0210/tutorial-video-skill★ 13same name
- Neelshah1810/Claude-Video-Skills★ 6same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository32 other files it gives its agents.
- grok-firstSkill
- opencode-firstSkill
- apple-design-accessibilitySkill
- apple-design-aiSkill
- apple-design-commerceSkill
- apple-design-connected-technologiesSkill
- apple-design-contentSkill
- apple-design-controlsSkill
- apple-design-foundationsSkill
- apple-design-gamesSkill
- apple-design-healthSkill
- apple-design-inputsSkill
- apple-design-iosSkill
- apple-design-mediaSkill
- apple-design-menusSkill
- apple-design-navigationSkill
- apple-design-patternsSkill
- apple-design-platformsSkill
- apple-design-presentationSkill
- apple-design-principlesSkill
- apple-design-privacy-identitySkill
- apple-design-spatialSkill
- apple-design-system-experiencesSkill
- design-maestroSkill
- forjaSkill
- product-studioSkill
- temperaSkill
- bambu-h2c-slice-printSkill
- bambu-print-troubleshootSkill
- blender-print-readySkill
- fdm-print-preflightSkill
- ads-videoSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 57
State between takes: every real take changes data.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Tutorial videos
Script → voice per scene → recording that holds each scene exactly as long as its narration → ffmpeg. No manual editing. Proven on a web portal, a Chrome extension popup and an Expo app (debug and release builds).
Ground rules (ask first, never assume)
- Real actions only on a test account, and only after the user says so. Recordings click for real (clock in, confirm, approve). Prefer a stage with mocks (mode B) when the account must stay untouched.
- Every real action has an undo. Before the first take, list each action the video performs and how it is undone (withdraw the request, delete the upload, remove the stamped period). Undo off camera after every take and verify it (screenshot or list). Rehearsals leave traces too — e.g. a time-tracking editor cannot save an empty day, so a rehearsal's entries cannot simply be removed. For irreversible flows plan rehearsal-free takes: rehearse up to the irreversible step (
FINAL=0), then one final take. - Never show credentials or personal data. The user signs in off camera (the driver waits for it). Blur e-mails (overlay does it). Names, avatars, company names, IDs: pixelate in post with fixed rectangles (pixelate, not blur), the same rectangles for stills and video. Dev logs may print inputs (passwords): never grep them into the conversation; redact log files.
- Language = native voice, not just translated text. ElevenLabs: pick a voice whose library language is the target language (a native speaker), not a premade English voice. OpenAI
gpt-4o-mini-ttskeeps an American accent unless the instruction says so. Working setup (German): voicemarin+ instruction "Sprache: Deutsch. Sprich wie eine deutsche Muttersprachlerin aus Deutschland, klares Hochdeutsch … kein amerikanischer Akzent … Speak German as a native speaker; no American accent." Let the user pick by ear (generate 4–6 voice samples of one sentence, publish them with audio players). - Takes: a take counts only after its contact sheet (6–8 frames,
scripts/contact-sheet.py) has been looked at. At most 3 takes per video; if the third fails, stop and report what breaks.
Layout in the project
<app>/tutorial/
script.<lang>.json title, subtitle, endTitle, endSubtitle, brand {color, logo, logoOnDark}, voice, scenes [{id, chapter, vo}]
record.mjs project-specific: what each scene does (copy a template)
stage.html mode B/C only (copy a template)
out/ git-ignored: vo/, frames/, timeline.json, the mp4Scripts live in this skill (scripts/), templates in templates/ (starting points: replace selectors, labels and routes with the app's own). Run scripts from a folder whose node_modules has playwright (or set PLAYWRIGHT_FROM).
Workflow
- Script (
templates/script.example.json): 6–10 scenes, one sentence or two each (≈6–12 s spoken). Scene ids drive the recorder'sACTIONS. Intro/outro are cards. Write the narration in the target language as a native would; captions show the same text. - Voice:
node <skill>/scripts/voice.mjs <lang> <tutorial-dir>→out/<lang>/vo/*.wav+vo.json(durations). Engines: ElevenLabs (default whenscript.voice.elevenlabs.voiceIdis set andELEVENLABS_API_KEYis found), OpenAI (OPENAI_API_KEY), orVOICE=sayfor a free macOS draft; force one withVOICE=elevenlabs|openai|say. Keys come from env, the nearest.envupwards, or the keychain (elevenlabs-api-key/openai-api-key) and are never printed. Silence at both ends is trimmed. ElevenLabs defaults toeleven_v4withlanguage_codeand text normalisation on (dates like "14.10." are spoken right); v4 takes only stability/similarity_boost. With a v2/flash model each scene also gets its neighbours (previous_text/next_text). The installedtext-to-speechskill (elevenlabs/skills) documents the API, models and settings.- Picking an ElevenLabs voice:
node <skill>/scripts/voice-samples.mjs <out-dir> de "<one sentence>"downloads the free previews of native German voices from the shared library and speaks the sentence with the account's own voices (credits). Publish the mp3s as a page with<audio>players; the user picks. A shared voice must be added to the account (POST /v1/voices/add/{public_owner_id}/{voice_id}) before TTS can use it — ask before adding. Modeleleven_v4(default, best);eleven_flash_v2_5is cheaper;eleven_multilingual_v2refuseslanguage_code. - Higgsfield (claude.ai Higgsfield connector, MCP) — useful when the own ElevenLabs account is on the Free plan.
list_voices→ pick an ElevenLabs preset voice (e.g. the German preset "Petra");generate_audio_batchwithmodel: "text2speech_v2",variant: "elevenlabs",voice_type: "preset",voice_id,prompt= the scene'svo;jobs_wait. Generate all clips of all videos in one session so the voice stays the same; at most 8 in parallel per batch, retry 429s in the next batch; about 0.31 credits per clip (Oct 2026). Download each mp3 as<clips>/<scene-id>.mp3, thennode <skill>/scripts/vo-from-files.mjs <lang> <tutorial-dir> <clips> --engine higgsfield-elevenlabs --target 45-55trims the silence, writes 48 kHz mono wavs andvo.json(the format the recorders and renderers read) and prints each clip's length (> 12 s flagged) and the projected video length. Job ids and voice ids stay out of the repo.
- Picking an ElevenLabs voice:
- Record (pick the mode):
- A · Web app, signed in —
scripts/driver.mjs <workdir> <sign-in URL> <signed-in path regex>opens Chrome for Testing headed; the user signs in; then drop command modules into<workdir>/cmd/(recon → off-camera reset →templates/record-web.mjs, e.g.export default async (a) => (await import("/abs/path/record-web.mjs")).default(a)). Crop the headed browser's white right/bottom strip at render:TUTORIAL_CROP=1896:1066:0:0. The recorder injectsscripts/overlay.js(cursor, ripple, ring, chapter, captions, cards; fill__BRAND__,__LOGO__with inline SVG markup,__END_TITLE__,__END_SUBTITLE__) and captures with CDPPage.startScreencast. - B · Extension popup / UI with mocks —
templates/stage-popup.html(a browser frame, the real built popup in an iframe) +templates/demo-mock.js(chrome.* and the API answered in the page, a movable clock__demo.setNow) +templates/record-stage.mjs. Deterministic, touches no account. - C · iOS app —
templates/record-sim.mjs:simctl io recordVideo+ taps by accessibility label via argent (describe/gesture-tap), or by coordinates (tapAt). Thenscripts/render-phone.mjscomposes the phone intotemplates/stage-phone.htmlwith chapter + captions beside it and a ring on every tap.- One dedicated simulator per job, its UDID in every call (
SIM_UDID; neverbooted— other jobs may own a booted device). Install a release-type simulator build from the cloud (CI/EAS simulator build +simctl install): no Metro, no dev menu, the real app. Checkuptimebefore each take — under heavy load frames drop and taps land late (the template warns when load > cores). - No element tree? A release build may expose none to argent until the app is relaunched, and a relaunch can sign the user out. Then tap by coordinates measured on a screenshot (
simctl io <udid> screenshot+sips -Z 900;tapAt(x, y)in that size), find moving rows by template matching (scripts/find-label.py), and verify every step with a screenshot (the template saves one per scene toout/<lang>/check/). - Pixelation: list rectangles per screen in
SCREENS(recording pixels), callenter(screen)when it changes; the windows land intimeline.blurand render-phone pixelates them.noBlurwindows switch them off while a full-screen sheet covers them.
- One dedicated simulator per job, its UDID in every call (
- A · Web app, signed in —
- Render: A/B
TUTORIAL_NAME=x node <skill>/scripts/render-frames.mjs <lang> <dir>; CTUTORIAL_NAME=x node <skill>/scripts/render-phone.mjs <lang> <dir>(re-encodessim.mp4withavconvertfirst, pixelatestimeline.blur). Output: H.264 1080p30, AAC, -16 LUFS, pad ducked 16–23 dB under the voice. - Review
python3 <skill>/scripts/contact-sheet.py out/<lang>/<name>.mp4 out/<lang>/contact.png --timeline out/<lang>/timeline.json [--phone], fix, re-take (undo the real actions off camera first), then publish (artifact with<video>players for the user; copy mp4 + poster where the product page needs it).
Stills (help pages, docs)
Capture raw screenshots on the same simulator, then python3 <skill>/scripts/pixelate-stills.py spec.json <out-dir>: per image a crop and the pixelation rectangles (raw pixels), output scaled to 1080 px wide, plus manifest.json (crop, rectangles, size, sha256). Keep the spec with the sources so a re-capture reproduces the same crop and rectangles; reuse the video's rectangles for the same screen.
Gotchas that cost hours
- Frame size: full Chrome's new headless crops the screencast to 993 px high — use the headless shell (
ms-playwright/chromium_headless_shell-*/…/chrome-headless-shell) for B; headed Chrome for Testing withdeviceScaleFactor: 1.25on a 1536×864 viewport gives ~1920×1080 for A (render crops/scales to exactly 1920×1080). - Sync: A/B start with a magenta flash; render finds the first frame after it. C:
recordVideowrites frames only on screen changes — the file starts at the spawn time (recStart) but ends at the last change, so never usestop − duration. - Waiting: dev servers compile on first visit (5–15 s). Wait for real content (
locator.waitFor), setpage.setDefaultTimeout(9000)while recording so a missing element skips instead of freezing 30 s; highlights must never block (boundingBox({timeout})in try/catch). - Selectors: sidebars often exist twice (desktop + hidden mobile sheet) →
a[href*="…"]:visible. Disabled rows (locked weeks) →:not([disabled]). Groups may need expanding first. Do a read-only recon module (buttons, dialogs, checkbox states, screenshot) before scripting a scene. - Dialogs in real apps often need extra input (a reason ≥3 chars, a "notices checked" box, times per day). Inspect them off camera; never tick a box you have not read (one take ticked "did not work this day").
- State between takes: every real take changes data. Write an off-camera
reset(end the running shift, confirm, close dialogs) and run it until the start state is back. "Später"/"later" dismissals may not persist across navigation. - Overlay:
addInitScriptaccumulates per take — the overlay removes older roots and the newest wins. Logos as<img>(path or data:) can be blocked by the app's CSP → inline SVG markup. Hide dev-only UI (TanStack devtools button, Expo tools FAB). - Expo dev client FAB (gear in every screenshot):
xcrun simctl spawn <udid> defaults write <bundle-id> EXDevMenuShowFloatingActionButton -bool NO, relaunch. Use the installed app's real bundle id (simctl listapps). - Deep links on a deep navigation stack lag or do nothing (modal open) → navigate in-app (back, tap rows); it also looks more natural.
- Simulator recordings mis-timed by ffmpeg: the H.264 from
simctl io recordVideois decoded with wrong timestamps after long still periods — pictures up to ~7 s out of place, a whole screen can vanish. Convert with macOSavconvert(--preset PresetHighestQuality, orPreset1920x1080) before ffmpeg touches it; render-phone does this. Any other ffmpeg step on a raw recording (joining two segments, extracting frames) must read the avconvert copy too. - Long off-camera waits (a clock that must run a few minutes): stop the recorder, wait, start a second recording, shift the second segment's timeline back by the gap and join the two avconvert copies — the video shows no wait and no frozen screen.
- Login detection on the simulator: don't guess from pixels (it tapped during the user's password entry); wait for the app's first signed-in request in the Metro log.
- ffmpeg 8 removed
-filter_complex_script→-/filter_complex <file>. Odd widths break yuv420p → scale/crop to 1920×1080. - Lockfiles: adding tooling to a pnpm workspace can re-resolve shared deps — check
git diff pnpm-lock.yaml | grep '^-[^-]'.
Other languages
Copy script.de.json → script.<lang>.json, translate as a native writer (not word order of the source), set the voice instruction for that language, run voice + render; re-record only when the UI language must change too.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

