xplainer
Skill5 starsChanged 2 months agoMITView on GitHub
Turn any problem — a bug, a root cause, an architecture decision, a new subsystem — into a short narrated explainer video with captions, rendered to a single MP4. Use when asked to "explain this as a video", "make an explainer", "turn this into a video", or when a fix is subtle enough that the team will understand it faster from ninety seconds of animation than from a diff.
In this repository57 other files it gives its agents.
- adversarial-reviewSkill
- aesthetic-animaxxingSkill
- ai-search-optimizationSkill
- announceSkill
- audio-trackSkill
- blindspotSkill
- brave-wesSkill
- chrome-wesSkill
- clihubSkill
- dashclaw-governanceSkill
- deskclawSkill
- de-vibeSkill
- dispatch-blocksSkill
- epic-infographicsSkill
- fable-gptSkill
- frontend-verifySkill
- gsap-astroSkill
- gsap-coreSkill
- gsap-frameworksSkill
- gsap-nextjsSkill
- gsap-nuxtSkill
- gsap-performanceSkill
- gsap-pluginsSkill
- gsap-react-routerSkill
- gsap-reactSkill
- gsap-scrolltriggerSkill
- gsap-sveltekitSkill
- gsap-tanstack-routerSkill
- gsap-timelineSkill
- gsap-utilsSkill
- gsap-vanillaSkill
- harness-healthSkill
- iphoneSkill
- landing-page-cro-expertSkill
- launchSkill
- launch-videoSkill
- logo-revealSkill
- marketingSkill
- marketing-studioSkill
- meditateSkill
- moleresearchSkill
- og-assetsSkill
- petstoreSkill
- phone-gotchasSkill
- phoneSkill
- preflightSkill
- product-demoSkill
- ship-itSkill
- shipSkill
- show-meSkill
- skillfindSkill
- social-clipSkill
- spec-probeSkill
- teamSkill
- tregSkill
- wes-voiceSkill
- wrapSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. About 0 are common, 8 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 21
Prefer the local tools when they are present: If this session has an xplainer server registered, call it: it renders on the machine the …
- Very specific: line 103
Keep import type on that first line.
- Distinctive: line 121
Get the explanation right: Read the code, the diff, the logs.
- Distinctive: line 126
Decide the visual direction: before writing components (below).
- Very specific: line 134
If the caller handed you screenshots, recordings, or diagrams, use them — real footage of the bug beats any abstraction you can draw.
- Distinctive: line 146
Before writing any scene, ask: *what would this look like if it were moving?*
- Distinctive: line 161
One mechanism per scene: If a scene needs "and also", it is two scenes.
- Distinctive: line 164
Movement must mean something: If an element moves, the movement is the point.
- Distinctive: line 206
Keep one focal point per frame, hold a generous margin, and keep scene content out of the band reserved for captions.
- Very specific: line 235
Prefer the scale, translate and rotate CSS properties over transform strings — they stay readable and editable.
- Very specific: line 238
Add video and audio with @remotion/media, images with Remotion's <Img>.
- Very specific: line 250
Use holdSeconds on a segment when the animation needs to finish after the voice stops.
- Distinctive: line 296
The workspace is a build area, not a repository: Treat the MP4 as the deliverable and hand it over directly, rather than expecting the …
≈ “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
xplainer
Build an explainer video: animated scenes, a synthesised voiceover, and word-level captions, rendered together into one MP4.
The bar is comprehension. Someone who watches the output should be able to say what broke, why it broke, and what the fix does — without opening the diff. That bar is met by showing the mechanism, not by narrating a slide deck.
One tool set, and it runs on this machine
The eight tools are served by a runtime on the user's own machine. Nothing is uploaded and nothing renders anywhere else: the workspace, the speech service and every frame stay where this session already is.
Prefer the local tools when they are present. If this session has an xplainer server registered, call it: it renders on the machine the user is already sitting at, it has no quota, and the finished MP4 lands somewhere they can open. If none is registered, say so and stop — there is no second route to reach for.
Rendering uses Remotion. Depending on the size of the user's company and how they use it, they may need their own Remotion licence — see https://remotion.pro/license.
Nothing to install
Remotion, React and Tailwind are already installed in the workspace the runtime
owns. You never run npm install, never scaffold a project, and never touch
node_modules. Everything goes through the tools:
| Tool | Does |
explainer_create(slug) | Makes the video and scaffolds its files |
explainer_put_source(slug, files) | Writes your Remotion source files into the video |
explainer_put_media(slug, name, base64) | Uploads one image, audio or video asset |
explainer_narrate(slug, narration) | Voiceover + captions + timings |
explainer_still(slug, frame) | One frame, for a layout check |
explainer_render(slug) | Renders the MP4 |
explainer_job(job_id) | Progress of any of the queued three |
explainer_list() | What videos exist and their state |
Narration, stills and renders are queued, so those three tools return a job_id
immediately — poll explainer_job rather than waiting.
explainer_create scaffolds six files. Five belong to the engine and you
cannot write them: index.ts, types.ts, Root.tsx, Captions.tsx and
Video.tsx. Video.tsx is the composition shell — it mounts the voiceover,
mounts the caption track, and lays out one <Sequence> per narration segment at
the measured position and length.
This is enforced, not requested. A write naming any of those five is refused, the whole call fails, and nothing at all is written — not even the files in the same call that were fine. A render restores an engine file that was altered by any other route. The reason is blunt: two of the lines in that shell are the soundtrack and the captions, and a video that lost them still renders, still exits clean, and ships silent.
The sixth file, Scenes.tsx, is yours, together with every component it
imports — by convention under scenes/ — and any helper, hook or data file you
want beside them. Nothing about what a scene may contain is constrained: a scene
is ordinary React and you can draw anything. Scenes.tsx is scaffolded once, as
an empty map, and never overwritten, so explainer_create stays safe to re-run.
Your files reach the runtime through explainer_put_source, one call carrying
every file you changed. Paths are relative to the video, so a scene component
goes to scenes/Symptom.tsx and the map naming it goes to Scenes.tsx. Only
those five exact top-level names are reserved — scenes/Root.tsx is yours.
explainer_create also returns a write_source_to path, and you may write the
files there directly instead; the same five names are off limits there too.
The one mechanic that isn't negotiable
explainer_narrate synthesises the voiceover and gets back the timestamp of
every spoken word. Every scene length, and every caption, comes from those
measurements, written to timings.json.
Guess durations instead and the picture drifts from the voice a few scenes in.
So: write the narration, narrate it, then build the picture against
timings.json. Never hand-write a scene duration.
You never write a scene's position or its length, because you never write the
<Sequence>. Scenes.tsx maps each narration segment id to the component that
draws it, and the engine reads from and durationInFrames straight off the
measured segment:
import type { SceneMap } from "./Video";
import { SymptomScene } from "./scenes/Symptom";
import { CauseScene } from "./scenes/Cause";
export const scenes: SceneMap = {
symptom: SymptomScene,
cause: CauseScene,
};Keep import type on that first line. As a value import it creates a real module
cycle at runtime and your scenes can come back undefined.
Each scene is handed { segment, timings, captions }. Mismatches are loud in
both directions: a key naming no narration segment fails the render and lists the
valid ids, and a segment with no key draws a visible "no scene" placeholder — so
building one scene at a time works, and reordering the narration reshuffles the
picture correctly on its own.
Two optional exports draw across the whole video if you want them: Backdrop,
behind every scene, and Overlay, above the scenes and below the captions.
Inside a scene, useCurrentFrame() restarts at zero, so scene animations are
written relative to the scene. To land a beat on a specific spoken word, read
captions.json — every word carries real startMs/endMs.
Workflow
- Get the explanation right. Read the code, the diff, the logs. You need the real root cause; an explainer built on a guess is worse than none, because it is confidently wrong and now memorable. Land on: the symptom someone noticed, what actually caused it, why it was non-obvious, what the fix changes. If there were three causes, that is the story.
- Decide the visual direction before writing components (below).
- Storyboard as motion — what moves, and what that movement means.
explainer_create, then write the narration andexplainer_narrate.- Design your scenes against
timings.json— one entry inScenes.tsxper narration segment id, plus the components it imports — and send them all withexplainer_put_source. explainer_stilland look at it, thenexplainer_render.
If the caller handed you screenshots, recordings, or diagrams, use them — real
footage of the bug beats any abstraction you can draw. Lead with it. Send each
file with explainer_put_media and reference it as staticFile("media/name.png");
explainer_create returns the matching put_media_in path, so you may also
drop the files there yourself.
Show the mechanism, don't list it
The default failure mode is a deck: heading, three bullets, cut to next heading. That transfers words, not understanding. People understand a bug when they watch the wrong thing happen.
Before writing any scene, ask: what would this look like if it were moving?
| When the point is… | Show |
| a sequence of events | a timeline that plays — events landing one by one, the late one arriving after the boundary that was supposed to close things out |
| state that got stuck | the state machine itself, a token moving through it, and stalling where it stalled |
| a race or an ordering bug | two lanes against one clock, drifting out of order |
| data taking the wrong path | the payload travelling between boxes and visibly branching wrong |
| a value that should have changed | the value on screen, changing — or conspicuously not |
| specific code | the excerpt held still, with emphasis moving to the line under discussion |
| before and after | one diagram transforming in place, not two static cards side by side |
| scale, cost, or volume | quantity as quantity: bars growing, a counter running, a queue filling |
Working rules:
- One mechanism per scene. If a scene needs "and also", it is two scenes.
- Hold after motion settles. Give each state about a second of stillness to be read. Animation that never rests is harder to follow than a static card.
- Movement must mean something. If an element moves, the movement is the point. Ambient drift, floating particles, and things sliding in for no reason read as generated filler.
- Reserve a slot for every element. Lay scenes out with flex or grid and fade elements in where they already sit. Never let a late arrival shove an earlier one sideways.
- Voice and picture do different jobs. The animation shows what happens; the narration explains why it happens. If the voice is reading the words on screen, delete one of them.
- Text is the fallback, not the default. A text card is right for a hard claim you want to land — a takeaway, a name, a number. It is wrong as the representation of a process.
Visual direction
Design it for this subject, the way a studio would. Do not reach for a generic "tech explainer" look.
Fix the direction before building. Write down, in a few lines:
- Palette — 4–6 named hex values. Derive them from the subject's own world where there is one: the product's real UI, the terminal it runs in, the colours the logs already use for pass and fail.
- Type — a display face with personality used with restraint, plus a body face that stays readable in motion, plus a mono face if code appears. Set a real scale; do not let one weight do every job.
- Motion signature — the one movement idea repeated throughout. Everything easing on the same curve, a single accent that travels between scenes, a consistent way new information arrives.
- Signature element — the one thing this video is remembered by. Spend your boldness here and keep everything around it quiet.
Then check the plan against the brief: if any part of it is what you would have produced for any engineering explainer, change that part.
Currently over-used and worth avoiding unless the brief actually asks: cream
background with a high-contrast serif and a terracotta accent; near-black with
one acid-green accent; glassmorphic cards floating on a gradient mesh; and
01 / 02 / 03 numbering on content that is not genuinely a sequence.
Sizing for a 1920×1080 frame: headline 90–120px, body 48–56px, labels 32px minimum. Anything smaller is decoration — if it must be read, make it bigger. Keep one focal point per frame, hold a generous margin, and keep scene content out of the band reserved for captions.
Styling: Tailwind is allowed
Tailwind v4 is installed and enabled workspace-wide. Use utility classes or inline styles — whichever suits the video. Both are fine; don't mix them arbitrarily within one video.
The engine's index.ts imports the stylesheet already, so the utilities are live
whether or not you reach for them — there is nothing to wire up, and nothing to
remove if you style purely inline.
One hard limit: Tailwind's animation and transition utilities — animate-*,
transition-*, duration-* — do not render. Remotion draws each frame
independently, so CSS-driven motion silently produces a static frame. Tailwind
is for layout, colour, spacing and type; motion is always interpolate(). This
is the single most common way a Remotion video comes out looking broken.
Note also that arbitrary Tailwind text sizes are usually easier to read at video
scale than the default scale — text-[96px] beats text-6xl when you want a
specific size.
Motion craft
- Animate with
useCurrentFrame()andinterpolate(). CSS transitions and CSS/Tailwind animations do not render. - Reach for
interpolate()withEasing.bezier()first;Easing.spring()when the motion should overshoot. - Prefer the
scale,translateandrotateCSS properties overtransformstrings — they stay readable and editable. - Stagger related elements by a few frames rather than moving them as a block.
- Add video and audio with
@remotion/media, images with Remotion's<Img>.
Narration
- Short sentences. Each becomes its own caption window; long clauses smear the timing.
- Spell out symbols. "the agent underscore id field", not
agent_id. "line twenty-eight sixty", not:2860. The model reads literally. - Open on the symptom, in the words someone would actually use to complain about it. Architecture comes after the viewer cares.
- Don't narrate the visuals. No "as you can see here".
- 60–150 seconds, roughly 8–14 segments. Longer than that, ship two videos.
- Use
holdSecondson a segment when the animation needs to finish after the voice stops.
The narration spec passed to explainer_narrate:
{
"voice": "af_heart",
"speed": 1.0,
"fps": 30,
"segments": [
{ "id": "symptom", "text": "The session sat there saying it was working." },
{ "id": "cause", "text": "A subagent outlived the stop event.", "holdSeconds": 6 }
]
}Voices are <language><gender>_<name> — af_heart (default) is American
female, am_ American male, bf_/bm_ British. Voices blend by weight:
"af_bella(2)+af_sky(1)". speed runs 0.25–4.0.
Pass dry_run: true to skip synthesis and get silence of estimated length — the
fast way to check pacing and layout before committing to real audio.
Before you render
explainer_still(slug, frame) and actually look at the image:
- Is the main message readable in about a second?
- Is there one obvious focal point, or is the frame competing with itself?
- Does anything overlap, or collide with the captions?
- Would this frame still make sense if it were on screen for half a second?
Remember a still can catch a scene mid-reveal — if it looks sparse, check whether later elements simply haven't arrived yet.
Then render, and report the output path or URL and the duration.
Notes
- Narration needs a speech service. It runs on the user's machine, and a
failed
explainer_narrateusually means it is not running yet — the runtime's own setup command installs and starts it. Read the failure out ofexplainer_jobbefore retrying. - Re-running is cheap. Change the narration and re-narrate, or change the components and re-render. There is no state to clean up.
- The workspace is a build area, not a repository. Treat the MP4 as the deliverable and hand it over directly, rather than expecting the sources to still be sitting there later.
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.

