Home/Features/portable-artifacts
plugin · 5 skills · installer

portable-artifacts

Dashboards and animated explainers from a prompt — built locally, offline, from open-source parts, by any coding agent.

Install → View source ↗
5
skills
4
hosts supported
0
cloud calls
1
JSON spec per artifact

What's new in 1.0.0

22 Oct 2026 · numbers, cheaper models and plan mode. The full list, with the older releases, is in the changelog.

Numbers on one lineA number with its unit or percent sign (1,204 ms, 18%) no longer splits across two lines in make-explainer pages and decks, make-dashboard KPI tiles or make-motion text. check.mjs fails on one that wraps.
Cheaper modelOn GitHub Copilot and Codex a build can run on a small artifact-builder agent file (Haiku 5.5 on Copilot, gpt-6-luna on Codex). After an npx skills install, doctor.mjs --fix builder_agent writes it. Measured on Copilot CLI only.
Plan modeThe make-explainer plan preset makes a page a reviewer answers in the browser, with two new themes, mono and brief. Answers come back as a checked plan-answers.json, or as the page itself through Save as HTML.
Security scannerThe two Socket anomaly alerts on make-diagram and make-explainer are fixed.
0.8.1 · 22 Oct 2026make-motion voiceover dependencies moved to patched versions.
0.8.0 · 21 Oct 2026make-motion: polish, crossfade, sound cues, texture and typing (below).

Earlier: 0.8.0, make-motion

21 Oct 2026 · make-motion. The full list, with the older releases, is in the changelog.

Theme colour tokensA shape fill or stroke and a text color can name a token of the active theme (surface, line, accent, accentSoft, good, bad, warn, text, text2, text3, cat1 to cat6), so a theme change recolours it.
Cards, round caps, arrowsA rect can be an elevated card (a lifted fill and a 1 px inner highlight); a line takes "cap": "round" and "arrow": "end".
Calmer chartsAbout five ticks per axis, no tick marks, no y-axis line, no vertical gridlines, rounded bars with a gap, 4 px round lines and ringed filled points.
Crossfade"transition": "crossfade" fades the new scene in over the previous one for 0.4 s; the previous one stays fully visible, so nothing dips to the background.
Sound cuesA cues list of pop, click, whoosh, chime and impact is synthesised locally, with the same bytes every time. A render with audio ends with a line giving the integrated loudness and true peak.
TextureAn opt-in vignette and static film grain that leaves a frame's average brightness unchanged (within 1 luma point at the strongest setting). At 1920×1080 on one Apple-silicon Mac a frame took about 17 to 33 ms with none, 42 ms with the vignette, 100 ms with grain and 187 ms with both.
Typing"typing": true types a line out at 0.035 s per character with a caret; its box is always the full text.
New check.mjs warningsA blank first frame (the video's thumbnail), named by cause, and text that overlaps across a crossfade. Neither fails the check.
SampleThe Relay teaser clip in the gallery uses all of the above.
0.7.0 · 19 Oct 2026make-scene arrived: explorable 3D cities, forests, galaxies and terrains, or a diagram or chart lifted into 3D.

01 Overview

Five agent skills that do what hosted "artifact" features do, without a cloud, an account or a particular model. You describe what you want; the agent writes a small JSON spec; local scripts check it and build one self-contained file you can open offline.

  • make-dashboard: a prompt and a data file become KPI tiles and charts (eleven chart kinds, live reload). Every chart shows its SQL, row count and refresh time.
  • make-diagram: a prompt becomes an architecture diagram, flowchart, sequence diagram or state diagram, with zoom, hover tracing and a story player that follows a request hop by hop.
  • make-explainer: a branch, an idea or a plan becomes a page or slide deck of verdicts, numbers, files changed, timelines and a test checklist that remembers its ticks. A linter rejects filler phrases and leftover placeholders before it builds.
  • make-motion: a prompt becomes a live preview with a scrubber and a 1080p MP4. It stays silent unless you ask for sound: music from a file you supply, short synthesised sound cues, or narration made locally with Kokoro.
  • make-scene: your data becomes an explorable 3D city, forest, galaxy or terrain, or a diagram or dashboard chart is lifted into 3D. Orbit, hover for exact values and follow a camera tour; the build offers the other worlds with a one-line command to switch.

Pages and clips use the dusk theme unless you pick press, poster, one of four older themes, or mono or brief (made for plan pages). The skills work with any agent that can read files and run shell commands, and an installer ports them to Codex, GitHub Copilot, Gemini CLI and Claude Code. Every page names the model, effort level and agent that wrote it, and each skill's doctor script reports anything missing before the first build.

The design is spec-first: the model writes one small JSON spec, and deterministic Node scripts do everything else. Field-level errors from the validator drive the retry loop.

02 See it in action

Real outputs, not mock-ups: each artifact below was produced by an agent (Claude Code) from the exact prompt shown, following the skill's SKILL.md. The card also shows the one follow-up line that asked for narration, because audio is never added unasked.

/make-motion — a 15-second 1080p explainer from one prompt, narrated on request

Download the MP4 ↓ (H.264 + AAC voiceover)

The exact prompt

Make a 15-second explainer, 1920x1080, titled "From prompt to dashboard". Scene 1: the title and the subtitle "Any agent. Offline. Open source." Scene 2: three steps appear one after another as rounded cards: "1 Write the spec", "2 Validate", "3 Build", with connecting lines drawing in between them. Scene 3: a bar chart reveal of validate errors per attempt (attempt 1: 3, attempt 2: 1, attempt 3: 0) with the caption "Field-level errors teach the model". Scene 4: a counter from 0 to 1 with the suffix " file", and the caption "One self-contained HTML page". End on a calm hold.

The follow-up (audio is opt-in)

Add a voiceover that reads each scene aloud.
Show the generated motion.json spec
{ "size": [ 1920, 1080 ], "fps": 30, "scenes": [ { "id": "title", "duration": 3, "elements": [ { "id": "title-text", "type": "text", "text": "From prompt to dashboard", "x": 960, "y": 480, "fontSize": 112, "fontWeight": 700, "keyframes": [ { "t": 0, "opacity": 0, "y": 520 }, { "t": 0.8, "opacity": 1, "y": 480, "ease": "outCubic" } ], "role": "title" }, { "id": "title-sub", "type": "text", "text": "Any agent. Offline. Open source.", "x": 960, "y": 610, "fontSize": 48, "fontWeight": 500, "keyframes": [ { "t": 0.5, "opacity": 0, "y": 640 }, { "t": 1.2, "opacity": 1, "y": 610, "ease": "outCubic" } ] } ], "background": "art" }, { "id": "steps", "duration": 4, "transition": "fade", "elements": [ { "id": "steps-card-1", "type": "shape", "shape": "rect", "x": 340, "y": 540, "width": 440, "height": 200, "radius": 28, "fill": "#241a38", "stroke": "#ff7f61", "strokeWidth": 3, "keyframes": [ { "t": 0.2, "opacity": 0, "scale": 0.9 }, { "t": 0.7, "opacity": 1, "scale": 1, "ease": "outCubic" } ] }, { "id": "steps-label-1", "type": "text", "text": "1 Write the spec", "x": 340, "y": 540, "fontSize": 42, "fontWeight": 600, "keyframes": [ { "t": 0.2, "opacity": 0 }, { "t": 0.7, "opacity": 1, "ease": "outCubic" } ], "role": "title" }, { "id": "steps-line-1", "type": "shape", "shape": "line", "x": 650, "y": 540, "width": 180, "stroke": "#ff7f61", "strokeWidth": 4, "keyframes": [ { "t": 0.8, "draw": 0 }, { "t": 1.3, "draw": 1, "ease": "inOutQuad" } ] }, { "id": "steps-card-2", "type": "shape", "shape": "rect", "x": 960, "y": 540, "width": 440, "height": 200, "radius": 28, "fill": "#241a38", "stroke": "#ff7f61", "strokeWidth": 3, "keyframes": [ { "t": 1.3, "opacity": 0, "scale": 0.9 }, { "t": 1.8, "opacity": 1, "scale": 1, "ease": "outCubic" } ] }, { "id": "steps-label-2", "type": "text", "text": "2 Validate", "x": 960, "y": 540, "fontSize": 42, "fontWeight": 600, "keyframes": [ { "t": 1.3, "opacity": 0 }, { "t": 1.8, "opacity": 1, "ease": "outCubic" } ] }, { "id": "steps-line-2", "type": "shape", "shape": "line", "x": 1270, "y": 540, "width": 180, "stroke": "#ff7f61", "strokeWidth": 4, "keyframes": [ { "t": 1.9, "draw": 0 }, { "t": 2.4, "draw": 1, "ease": "inOutQuad" } ] }, { "id": "steps-card-3", "type": "shape", "shape": "rect", "x": 1580, "y": 540, "width": 440, "height": 200, "radius": 28, "fill": "#241a38", "stroke": "#ff7f61", "strokeWidth": 3, "keyframes": [ { "t": 2.4, "opacity": 0, "scale": 0.9 }, { "t": 2.9, "opacity": 1, "scale": 1, "ease": "outCubic" } ] }, { "id": "steps-label-3", "type": "text", "text": "3 Build", "x": 1580, "y": 540, "fontSize": 42, "fontWeight": 600, "keyframes": [ { "t": 2.4, "opacity": 0 }, { "t": 2.9, "opacity": 1, "ease": "outCubic" } ] } ] }, { "id": "errors", "duration": 4, "transition": "fade", "elements": [ { "id": "errors-heading", "type": "text", "text": "Validate errors per attempt", "x": 960, "y": 150, "fontSize": 56, "fontWeight": 600, "keyframes": [ { "t": 0, "opacity": 0 }, { "t": 0.5, "opacity": 1, "ease": "outCubic" } ], "role": "title" }, { "id": "errors-chart", "type": "chart", "x": 960, "y": 520, "width": 1000, "height": 520, "vegaLite": { "data": { "values": [ { "attempt": "Attempt 1", "errors": 3 }, { "attempt": "Attempt 2", "errors": 1 }, { "attempt": "Attempt 3", "errors": 0 } ] }, "layer": [ { "mark": { "type": "bar", "size": 120, "cornerRadiusEnd": 8, "color": "#ff7f61" } }, { "mark": { "type": "text", "dy": -24, "fontSize": 36, "fontWeight": 600, "color": "#f7ebe0" }, "encoding": { "text": { "field": "errors", "type": "quantitative" } } } ], "encoding": { "x": { "field": "attempt", "type": "ordinal", "axis": { "title": null, "labelAngle": 0, "labelFontSize": 28 } }, "y": { "field": "errors", "type": "quantitative", "scale": { "domain": [ 0, 4 ] }, "axis": { "title": null, "tickMinStep": 1, "labelFontSize": 24 } } } }, "keyframes": [ { "t": 0.4, "reveal": 0 }, { "t": 2.2, "reveal": 1, "ease": "inOutCubic" } ] }, { "id": "errors-caption", "type": "text", "text": "Field-level errors teach the model", "x": 960, "y": 900, "fontSize": 52, "fontWeight": 500, "keyframes": [ { "t": 2.2, "opacity": 0, "y": 925 }, { "t": 2.9, "opacity": 1, "y": 900, "ease": "outCubic" } ] } ] }, { "id": "one-file", "duration": 4, "transition": "fade", "elements": [ { "id": "file-counter", "type": "counter", "from": 0, "to": 1, "suffix": " file", "x": 960, "y": 440, "fontSize": 220, "fontWeight": 700, "keyframes": [ { "t": 0.3, "value": 0 }, { "t": 1.5, "value": 1, "ease": "outCubic" } ] }, { "id": "file-caption", "type": "text", "text": "One self-contained HTML page", "x": 960, "y": 700, "fontSize": 60, "fontWeight": 500, "keyframes": [ { "t": 1.4, "opacity": 0, "y": 730 }, { "t": 2.2, "opacity": 1, "y": 700, "ease": "outCubic" } ], "role": "title" } ], "background": "art" } ], "audio": [ { "src": "voice/title.wav", "scene": "title", "at": 0.3 }, { "src": "voice/steps.wav", "scene": "steps", "at": 0.3 }, { "src": "voice/errors.wav", "scene": "errors", "at": 0.3 }, { "src": "voice/one-file.wav", "scene": "one-file", "at": 0.3 } ], "theme": "dusk" }

/make-dashboard — a growth dashboard from two CSV files

Open full screen ↗

The exact prompt

Build a "Growth overview" dashboard from data/sales.csv and data/web_traffic.csv: KPI tiles for total revenue, units sold and overall signup conversion rate; revenue by region; the monthly revenue trend; daily sessions by channel; and signup conversion rate by channel.
Show the generated dashboard.json spec
{ "title": "Growth overview", "description": "Sales and web traffic: revenue, units, sessions and signup conversion", "maxRows": 5000, "sources": { "sales": { "type": "csv", "path": "data/sales.csv" }, "web_traffic": { "type": "csv", "path": "data/web_traffic.csv" } }, "queries": { "sales_total": "SELECT SUM(amount) AS revenue, SUM(units) AS units FROM sales", "conversion_total": "SELECT SUM(signups) * 1.0 / SUM(sessions) AS conversion FROM web_traffic", "rev_by_region": "SELECT region, SUM(amount) AS revenue FROM sales GROUP BY 1 ORDER BY 2 DESC", "rev_by_month": "SELECT date_trunc('month', date) AS month, SUM(amount) AS revenue FROM sales GROUP BY 1 ORDER BY 1", "sessions_by_channel": "SELECT date, channel, SUM(sessions) AS sessions FROM web_traffic GROUP BY 1, 2 ORDER BY 1, 2", "conversion_by_channel": "SELECT channel, SUM(signups) * 1.0 / SUM(sessions) AS conversion FROM web_traffic GROUP BY 1 ORDER BY 2 DESC" }, "kpis": [ { "id": "k1", "title": "Total revenue", "query": "sales_total", "field": "revenue", "format": "currency" }, { "id": "k2", "title": "Units sold", "query": "sales_total", "field": "units", "format": "number" }, { "id": "k3", "title": "Signup conversion rate", "query": "conversion_total", "field": "conversion", "format": "percent" } ], "charts": [ { "id": "c1", "title": "Revenue by region", "query": "rev_by_region", "width": "half", "vegaLite": { "mark": "bar", "encoding": { "x": { "field": "region", "type": "nominal", "sort": null, "title": null }, "y": { "field": "revenue", "type": "quantitative", "title": "Revenue" } } } }, { "id": "c2", "title": "Signup conversion rate by channel", "query": "conversion_by_channel", "width": "half", "vegaLite": { "mark": "bar", "encoding": { "x": { "field": "channel", "type": "nominal", "sort": null, "title": null }, "y": { "field": "conversion", "type": "quantitative", "title": "Conversion rate", "axis": { "format": ".1%" } } } } }, { "id": "c3", "title": "Monthly revenue", "query": "rev_by_month", "vegaLite": { "mark": { "type": "line", "point": true }, "encoding": { "x": { "field": "month", "type": "temporal", "timeUnit": "utcyearmonth", "title": null }, "y": { "field": "revenue", "type": "quantitative", "title": "Revenue" } } } }, { "id": "c4", "title": "Daily sessions by channel", "query": "sessions_by_channel", "vegaLite": { "mark": "line", "encoding": { "x": { "field": "date", "type": "temporal", "timeUnit": "utcyearmonthdate", "title": null }, "y": { "field": "sessions", "type": "quantitative", "title": "Sessions" }, "color": { "field": "channel", "type": "nominal", "title": "Channel" } } } } ] }

Generated by Claude Code following SKILL.md from the prompt shown, then the follow-up, which added only the audio field. The clip was later re-rendered in the new themes (see the note under the Gallery). See all samples →

03 Problem & why it's needed

The friction

Hosted artifact features lock you to one vendor, need an account and the network, and regenerate whole pages of code for every small tweak — so "make the second scene slower" can change everything else too.

Why it mattersWriting JSON that matches a schema is a small job that weak and local models can do. A change is one field in the spec, never regenerated code, and the same spec always produces the same page or video (the footer date and a dashboard's refresh time aside). After a one-time install nothing touches the network except your own data sources, unless you opt in to make-explainer's --image-api hero image.

04 Install

Claude Code — add the marketplace, then install portable-artifacts

/plugin marketplace add abhijitbansal/claude-skills
/plugin install portable-artifacts@claude-skills

Then ask for a dashboard, a diagram, an explainer, a 3D scene or a video, or call a skill directly: /make-dashboard …, /make-diagram …, /make-explainer …, /make-scene … or /make-motion …. On first use the agent runs the skill's doctor and, once you agree, installs the dependencies (npm ci inside the skill folder; npm ci --omit=dev for make-dashboard, make-diagram, make-explainer and make-scene).

Install with npx skills

npx skills add abhijitbansal/claude-skills --skill make-dashboard make-diagram make-explainer make-motion make-scene
npx skills add abhijitbansal/claude-skills --skill make-dashboard make-diagram make-explainer make-motion make-scene -a claude-code codex github-copilot -y

The second form installs to Claude Code, Codex and GitHub Copilot without prompting. On first use the agent runs the skill's doctor and, once you agree, npm ci in the skill folder (npm ci --omit=dev for make-dashboard, make-diagram, make-explainer and make-scene). make-explainer needs make-diagram and make-dashboard beside it, and npx skills does not resolve dependencies, so name all three, as above. Without them a section that needs one fails validation and prints that exact command. To also run builds on a cheaper model on Copilot or Codex, the same doctor can write the optional artifact-builder agent file (see "Cheaper model" below): node <skill-dir>/scripts/doctor.mjs --fix builder_agent.

Codex, GitHub Copilot, Gemini CLI — or Claude Code without the marketplace

Each host reads skill folders from its own directory, and the skill itself is the slash command. The installer copies all five skills to the right place and installs their dependencies. It reads each skill's requires list, so installing make-explainer or make-scene also installs make-diagram and make-dashboard.

git clone https://github.com/abhijitbansal/claude-skills
node claude-skills/plugins/portable-artifacts/bin/install.mjs codex # ~/.agents/skills
node claude-skills/plugins/portable-artifacts/bin/install.mjs copilot --project . # ./.github/skills
node claude-skills/plugins/portable-artifacts/bin/install.mjs gemini # ~/.gemini/skills + commands
node claude-skills/plugins/portable-artifacts/bin/install.mjs claude # ~/.claude/skills, no marketplace

Hosts and where skills land

Claude Codeinstall.mjs claude · user ~/.claude/skills · project .claude/skills · invoke /make-dashboard …
Codex CLIinstall.mjs codex · user ~/.agents/skills · project .agents/skills · invoke $make-dashboard … (or /skills)
GitHub Copilot (CLI + VS Code)install.mjs copilot · user ~/.copilot/skills · project .github/skills · invoke /make-dashboard …
Gemini CLIinstall.mjs gemini · user ~/.gemini/skills + ~/.gemini/commands/*.toml · project .gemini/… · invoke /make-dashboard …
Anything reading .agents/skillsinstall.mjs agents · user ~/.agents/skills · project .agents/skills · invoke host-specific

Tested hosts

Claude Code and GitHub Copilot CLI were tested live. Codex CLI passed an earlier eval (dashboard 10/10, motion 6/10; four prompts hit a usage limit). Gemini CLI install paths were checked against its documentation but not run live. make-scene: 10/10 valid builds on the first run (Claude Haiku in Claude Code, median 1.5 validate runs, median 40 s), in eval/results/summary-scene-2026-10-19.md.

Test details

Claude Code and GitHub Copilot CLI 1.0.98 ran one non-interactive pass per skill on 2026-10-15: each picked up its skill, ran the doctor, validated and built a page. The browser check (check.mjs) was not run by an agent there, and consent was granted in the prompt, so the ask-first step was not exercised. Codex CLI 0.145.0 ran an earlier eval on 2026-10-10: dashboard 10/10, motion 6/10, with the last four motion prompts stopped by the account's usage limit. Results are in eval/results/ (summary-full-2026-10-10.md, summary-portability-copilot-2026-10-15.md, summary-install-targets-2026-10-15.md).

Installer flags

--project <dir>install into that project instead of your home directory
--skills make-motioninstall just one skill (plus the skills it requires)
--linksymlink to this checkout so git pull updates it
--forcereplace an existing install
--no-depsskip npm ci

Any other agent

If it can read files and run shell commands: copy a skill folder into your project (together with its shared/ folder), run npm ci inside it (npm ci --omit=dev for make-dashboard, make-diagram, make-explainer and make-scene; copy make-diagram and make-dashboard too if you use make-explainer or lift with make-scene), and add one line to your AGENTS.md (or .github/copilot-instructions.md).

To build a dashboard, a diagram, an explainer page, a 3D scene or an animated video, read and follow skills/make-dashboard/SKILL.md, skills/make-diagram/SKILL.md, skills/make-explainer/SKILL.md, skills/make-motion/SKILL.md or skills/make-scene/SKILL.md.

05 Requirements

Node.js ≥ 20all five skills
doctor.mjs (optional)in each skill, needs only Node: lists what is missing with the fix for your OS; the agent runs it first and asks before installing
npm ci (once)in each skill folder; the agent runs it through the doctor once you agree (--omit=dev is enough for make-dashboard, make-diagram, make-explainer and make-scene)
Sibling skillsmake-explainer — make-diagram and make-dashboard beside it for diagram and chart sections and card icons; make-motion only to render a motion spec (an existing .mp4 needs nothing); make-scene — make-diagram only to lift a diagram and make-dashboard only to lift a chart. git on PATH for gather.mjs
Chromium (once, ≈150 MB)make-motion — npx playwright install chromium inside the skill folder (Linux: add --with-deps); falls back to an installed Google Chrome. make-diagram, make-explainer and make-scene build without a browser; only the optional check.mjs uses one (needs a full npm ci) and skips cleanly without it, printing the doctor command that fixes it
ffmpeg on PATHmake-motion only — brew install ffmpeg (macOS) / winget install Gyan.FFmpeg (Windows) / sudo apt-get install ffmpeg (Linux), or set FFMPEG_PATH (≥ 4.4 for audio tracks)
kokoro-js (on demand, ≈250 MB + a 90 MB voice model once)make-motion voiceover only — the agent asks before installing; offline after the one-time model download

make-motion tools by OS

Playwright itself comes with npm ci. Run the Chromium download inside the make-motion skill folder, so it matches the Playwright version the lockfile pins.

macOS (Homebrew)

brew install node
brew install ffmpeg
npx playwright install chromium

Run the npx line inside the make-motion skill folder.

Windows (PowerShell)

winget install OpenJS.NodeJS.LTS
winget install Gyan.FFmpeg
npx playwright install chromium

Open a new terminal after winget so node and ffmpeg are on PATH. Run the npx line inside the make-motion skill folder; if PowerShell refuses npx.ps1 (scripts disabled), use npx.cmd playwright install chromium. choco install ffmpeg or scoop install ffmpeg also work.

Linux (Debian/Ubuntu)

sudo apt-get install ffmpeg
npx playwright install --with-deps chromium

Run the npx line inside the make-motion skill folder. Node 20+ from nodejs.org or a version manager; distro nodejs packages are often older. --with-deps installs the system libraries Chromium needs (asks for sudo).

06 Usability guide

/make-dashboard — a dashboard from a data file

You say "Revenue by region and the monthly trend from data/sales.csv". The agent inspects the data, writes dashboard.json (sources, SQL queries, KPIs, charts), validates it, and builds one offline index.html. Every chart expands to show its SQL, row count and refresh time. Rerun one command to refresh — no LLM involved. Sources: CSV, Parquet, JSON, DuckDB, SQLite, Postgres and MySQL.

Chart kindsbar, stacked-bar, grouped-bar, line, area, scatter, bubble, heatmap, histogram, boxplot, donut: name the columns, the build writes the chart. Raw Vega-Lite still works for anything else.
Viewsalternate views per chart (sorted, share, 100%, area, boxplot, …), a sortable Table view, and pair to show two side by side.
Exportper-card PNG, SVG and CSV from an Export menu.
Themespaper (default), blueprint, editorial, ink, the newer dusk, press and poster, and the plan-page themes mono and brief, plus an icon toggle on the page that cycles Auto, Light and Dark.
Live reloadrebuilds when you save the spec or the data and reloads the page; Postgres and MySQL need --interval. A failed build shows a banner and keeps the last good page.
node <skill-dir>/scripts/serve.mjs --watch dashboard.json --open
A make-dashboard page in light mode: four KPI tiles and four charts, each with a Query disclosureThe same dashboard in dark mode
/make-dashboard revenue by region and the monthly trend from data/sales.csv

/make-diagram — an architecture, flow, sequence or state diagram

You say "Draw the checkout request path from the browser to the orders DB". The agent writes diagram.json (a title, a type, nodes and edges, or participants and messages), validates it, and builds one offline HTML page under ~/.agent/diagrams/. Validation lays the diagram out and lints the geometry, so a spec that passes is buildable. "Add a queue between the API and the worker" changes a few fields.

Typesarchitecture (nested groups, 11 node kinds), flow, state and sequence (frames and notes).
In the pagewheel or pinch to zoom, drag to pan, 0 or double-click to fit, F full screen, hover to trace a node's edges, A for edge-flow animation, light and dark, SVG and PNG export.
Storiespress P to play the story: a moving camera and a travelling dot, one beat at a time. Space plays and pauses, arrows step the beats, Esc leaves. Beats come from the spec's stories or are built from the layout.
Explorepress E, click a node for its details, shift-click a second node for the route between them, / to find.
Icons306 Lucide icons and 126 software brand marks, drawn monochrome; the agent finds one with icons.mjs search.
Size12 nodes or fewer reads best; the limit is 60 nodes and 120 edges.
/make-diagram Draw the checkout request path from the browser to the orders DB

/make-explainer — a recap, checklist, plan, explainer or slide deck

You say "Recap this branch for review, with a manual test checklist". A script reads the git branch (commits, files changed with line counts, test files) and scaffolds an explainer.json with the numbers, file list and timeline filled in. The agent replaces each TODO: with prose, validates, and builds one offline HTML page under ~/.agent/diagrams/. Validation fails on leftover placeholders, filler phrases such as "leverage" or "seamless", numbered titles, and (for slides) a slide that is too full. Emoji are allowed; dense emoji only warn. Section titles state the finding, not a label; the short label is what the contents list shows.

Presetsrecap, checklist, concept, slides and plan, each with a working example in the skill.
Sections24 types: tldr, text, kpis, cards, table, risks, files, steps, timeline, compare, callout, code, details, checklist, diagram, chart, motion, scene, image, and for plans decision, approval, question, review and verdict.
Short on purposethe page opens with a hero and a TL;DR card; any section takes a label of up to 4 words for the contents list; validation warns, and never fails, over the soft caps (title 8 words, label 4, paragraph 3 sentences or 60 words, text section 120, card text 30, list item 20, page prose 900, tl;dr 3 points).
One-line numbersa number with its unit or percent sign (1,204 ms, 18%) is kept on one line. Explainer pages and decks wrap each token in a no-wrap span, and check.mjs fails on one that wraps. make-dashboard KPI values are no-wrap and sized to fit their tile; a value over 17 characters may wrap instead of spilling. make-motion text uses a no-break space, with the same kind of check.
Checkliststicks saved in your browser, a progress bar, per-group counts, a Reset that asks first, and a "What's in this branch" intro for the tester.
Plan modeask for a plan, proposal or design to be reviewed and answered. The page is decision first: it opens with a summary and three numbers, then decisions, approvals and open questions the reviewer answers in place, with "review these first" links, goals, phases and risks. It ends with the reviewer's overall verdict, a name, and the buttons to press: Copy answers, Download plan-answers.json or Save as HTML (a sticky button in the left menu that downloads the page with the answers inside; answers.mjs reads it too, but open only saved pages you trust). Answers are saved in that browser, keyed to a hash of the plan, so answers to an edited plan are flagged rather than reused. The agent checks the file with answers.mjs and acts only on a summary that is not marked invalid. The plan preset defaults to the mono theme.
Slidesone section per slide; arrows or Space to move, O for an outline, #slide-3 deep links, one slide per printed page.
Embedsmake-diagram figures whose Present button opens the full viewer: stories, Explore and full screen, make-dashboard charts as static SVG with a data table (local files only), make-scene 3D scenes (a card in a 16:9 frame, up to 4 per page), and video clips that never autoplay.
Themesdusk (default): Funnel Display, warm gradients, grain; press: Young Serif on bone paper, for dense docs, long reads and branch recaps; poster: condensed capitals, lime and orange, for launch decks and pitches. A theme you name wins (--theme poster or "use poster"). Otherwise the agent picks one from the prompt and says which and why in one line; if the prompt fits two equally it asks once, and if it cannot ask it uses dusk. paper, blueprint, ink, editorial, mono (terminal-style; a cyan accent in dark mode) and brief (executive, warm white with burgundy) are used only when named; a plan defaults to mono.
Artthe new themes draw seeded SVG art from the title in the page hero and on a deck's first and last slides: no network, and the same title gives the same picture.
Emojiallowed. Validation warns, never fails, when a prose string has 3 or more emoji at more than 1 per 40 characters, or a section title has more than 3.
Hero image (optional)build.mjs --image-api <https-endpoint> replaces the page hero's art with a picture from an image service you choose. Off by default. It sends the page title and subtitle off your machine to that host, needs your key in PA_IMAGE_API_KEY, and each build is one request your service may bill. Page hero only, in dusk, press or poster; a deck or any other theme is refused before anything is sent.
node <skill-dir>/scripts/gather.mjs git --out facts.json
node <skill-dir>/scripts/scaffold.mjs recap --facts facts.json --out explainer.json
node <skill-dir>/scripts/validate.mjs explainer.json
node <skill-dir>/scripts/build.mjs explainer.json
node <skill-dir>/scripts/build.mjs explainer.json --model "Claude Opus 5.5" --effort high --agent "Claude Code"
node <skill-dir>/scripts/scaffold.mjs plan --title "My plan" --out plan.json
node <skill-dir>/scripts/answers.mjs plan.json plan-answers.json
/make-explainer Recap this branch for review, with a manual test checklist

/make-motion — an animated explainer

You say "8-second clip: Q3 revenue up 18%, then a bar chart of Q1–Q3". The agent writes motion.json, validates it, opens a live preview with a scrubber, and renders a 1080p MP4 that comes out the same every time. "Slow scene 2" changes one field, not the whole video.

Audio is opt-in. Say "with background music from music.mp3" or "with a voiceover reading it". Narration is made locally with Kokoro, so no file is needed for it. Short UI sounds need no file either: a cues list of pop, click, whoosh, chime and impact is synthesised at render time, gives the same bytes every time, and the render ends with a line giving the integrated loudness and true peak.

Polish. Shapes and text can name theme colour tokens (surface, line, accent, cat1 to cat6 and more) that follow the theme; a rect can be an elevated card; a line takes a round cap and an arrow head. Charts default to about five ticks per axis, no tick marks, no vertical gridlines, rounded bars and 4 px round lines. A scene can crossfade over the previous one in 0.4 s, and a text can type itself out at 0.035 s per character.

Texture is opt-in. A vignette and static film grain (0 to 0.15) can go over every scene. They slow rendering: measured at 1920x1080 on one Apple-silicon Mac, about 17 to 33 ms per frame with none, 42 ms with the vignette, 100 ms with grain and 187 ms with both.

Themes. Themes follow the same rules as make-explainer, and a spec that names none renders in dusk. Colours a spec sets still win. For a themed look, mark a heading "role": "title" and give a scene "background": "art".

A make-motion render: a +18% counter, then animated revenue barsA make-motion explainer titled how it works
/make-motion 8-second clip: Q3 revenue up 18%, then a bar chart of Q1-Q3

/make-scene — your data as a 3D city, forest, galaxy or terrain

You say "Show our commits per week per folder as a 3D city, with a tour of the busiest weeks". The agent gets the rows (a script reads the git history, or it uses your CSV or JSON), writes scene.json (a title, data or a lift, and a short camera tour), validates it, and builds one offline three.js page. It then runs check.mjs, which opens the page at desktop and phone width and fails on a blank canvas or a tour that cannot finish. The agent never writes three.js, coordinates, CSS or HTML, and the spec accepts none. Switching to another world is one command that spends no model tokens.

Worldscity (a district per group, height is value), forest (a tree per group, parents grow sub-trees), galaxy (a star per group, links are trails) and terrain (a group by time grid as a landscape).
Liftsa make-diagram diagram of any type, or one chart of a make-dashboard file, redrawn in 3D. A lift needs that skill installed beside make-scene and has no alternative worlds.
Recommend, build, offerthe build picks the world that fits the data and prints a table: each world, why it fits, the page size, a render weight and the exact command to switch.
In the pagedrag to orbit, hover for the exact value, Explore for a list, Play or Space for the tour, F for full screen, PNG export. Height scales start at zero. Without WebGL the page shows the data table, or the 2D drawing for a lift.
Embeda make-explainer page takes a scene section, up to 4 per page.
node <skill-dir>/scripts/rows.mjs git --weeks 12
node <skill-dir>/scripts/validate.mjs scene.json
node <skill-dir>/scripts/build.mjs scene.json --out ./scene.html
node <skill-dir>/scripts/build.mjs scene.json --world forest --out ./scene.html
node <skill-dir>/scripts/check.mjs scene.html
/make-scene Show our commits per week per folder as a 3D city, with a tour of the busiest weeks

Cheaper model on Copilot and Codex

Writing a spec is a small job, so the installer can also write a small artifact-builder agent file that names a cheap model, and each skill has a "Cheaper model" section that tells the running agent to hand the work to it. Two ways to use it: pick the agent yourself (copilot --agent artifact-builder; the runs below used copilot -p with that flag) or let the skill hand off (advisory: the main agent may not do it). Claude Code reads model: haiku from each SKILL.md per its docs; that was not measured here.

MeasuredCopilot CLI 1.0.98, 2026-10-21, with eval/run-eval.mjs --agent artifact-builder: all 10 make-motion prompts and 9 runs across the other four skills built a valid page, and Copilot's own logs show claude-haiku-5.5 from the custom agent in all 19 runs.
Not testedCodex (the agent file comes from the Codex docs; there was no account to run it) and VS Code Copilot Chat. The skill hand-off ran in one trial; its sub-agent was denied file access and the main agent finished the work.
node plugins/portable-artifacts/bin/install.mjs copilot
node plugins/portable-artifacts/bin/install.mjs copilot --agents-only
node plugins/portable-artifacts/bin/install.mjs codex --agents-only

Installed with npx skills? That copies skill folders only, so there is no installer. Each skill carries its own writer instead: where ~/.copilot or ~/.codex exists, the doctor lists the missing agent file as optional, and after you agree this writes it at user level (an existing file is kept). A host may need a new session before it lists the agent.

node <skill-dir>/scripts/doctor.mjs --fix builder_agent

Details, the per-host table and the results: USAGE.md.

Every page: favicon, theme toggle and who made it

Each page has an inline favicon and a sun, moon or auto button that cycles Auto, Light and Dark, even when the browser blocks storage. The make-motion preview is a dark-only local tool, so it has no toggle. A footer line (under the title in a diagram) and a <meta name="generator"> record the model, effort level and agent that made the page.

Fonts per theme and the stamp fields

Fonts are bundled with the skills: dusk Funnel Display, Host Grotesk and Commit Mono; press Young Serif, Schibsted Grotesk and JetBrains Mono; poster Big Shoulders Display, Familjen Grotesk and Martian Mono; mono JetBrains Mono throughout, in regular weight only; brief Newsreader headings, an Atkinson Hyperlegible body and Geist Mono; the older themes Geist and Geist Mono, with Bricolage Grotesque headings in ink and Newsreader and Atkinson Hyperlegible in editorial.

The agent sets generatedBy: { model, effort, agent } in the spec, or you override per field with --model, --effort and --agent on the build. Each is 1 to 60 characters from A-Za-z0-9 ._:/()+-; anything else fails validation. A field nobody knows reads "not reported", never a guess. In make-dashboard, put generatedBy in dashboard.json, because serve.mjs --watch takes no flags. make-motion writes the stamp into the MP4's comment tag and, when the model is reported, into artist.

Before the first build: doctor

Each skill has a scripts/doctor.mjs that needs only Node, so it runs before npm ci. It lists what is missing, with the size and the fix for your OS. The agent runs it first, tells you what it found in one message and asks once; on yes it installs what it reported. Anything that needs sudo or has no automatic fix comes back to you as the exact command to run (in Claude Code, prefix it with !). On no, the skill does what it can without it.

Doctor flags and exit codes

--for takes a spec .json (every skill; only make-explainer and make-scene add sister skills from it: make-explainer for its diagram and chart sections, make-scene make-diagram or make-dashboard when the spec lifts one), check (make-diagram, make-explainer and make-scene) or voiceover (make-motion). --fix resolves its ids against the same --for: an id that target does not list is refused as "unknown id", and without --for it accepts every id the skill could need. Under --for check, dev_deps (Playwright) and chromium are required. make-explainer's browser for a motion section is motion_chromium, installed through make-motion. --fix exits 1 when any requested id is refused, skipped, failed, still missing or installed elsewhere.

node <skill-dir>/scripts/doctor.mjs --json
node <skill-dir>/scripts/doctor.mjs --json --for explainer.json
node <skill-dir>/scripts/doctor.mjs --json --for check
node <skill-dir>/scripts/doctor.mjs --fix node_modules
node <skill-dir>/scripts/doctor.mjs --for check --fix dev_deps,chromium

Codex syntax

Codex invokes skills with $: $make-dashboard …, $make-diagram …, $make-explainer …, $make-scene … or $make-motion … (or browse with /skills).

07 Security model

Dashboards run model-written SQL — read-only, boxed in

Each query must parse (DuckDB's own parser) as a single SELECT, and an allowlist over its syntax tree lets it read only the declared sources and its own CTEs: no query(), read_*, duckdb_*, system catalogs or sqlite_*/postgres_*/mysql_* bridges. It runs wrapped in a row limit and a timeout, against a DuckDB connection locked to the exact declared files (allowed_paths, external access off, configuration locked). Sources must live under the spec's folder unless you pass --allow-outside. For Postgres and MySQL, READ_ONLY only limits DuckDB: use a read-only database role. Credentials come only from environment variables and are scrubbed from errors and results as defence in depth; the SQL allowlist is the control.

Built pages embed the query results

Publishing index.html publishes the data.

No network from specs

Chart url/data/usermeta keys are rejected at any depth; motion colours can't be url(); pages run under a CSP without unsafe-eval; the renderer aborts any non-local request; local servers bind 127.0.0.1 and check the Host header.

The optional hero image sends text off your machine

Only make-explainer's --image-api <https-endpoint> flag uses the network, and only when you pass it. It sends the page title and subtitle to the host you name, with your key from PA_IMAGE_API_KEY, and the build says so before it sends. Each build with the flag is one request your image service may bill. The key is never written to the page, the reply must be a PNG, JPEG or WebP image, and any failure stops the build.

08 Licences

Everything this plugin depends on is open source.

DuckDB (@duckdb/node-api)MIT
Vega, Vega-Lite, vega-embed, vega-interpreterBSD-3-Clause
anime.js v4MIT
three.jsMIT
PlaywrightApache-2.0
AjvMIT
ELK (elkjs)EPL-2.0
Lucide iconsISC
Simple Icons (brand marks)CC0-1.0
Funnel Display, Host Grotesk, Commit Mono, Young Serif, Schibsted Grotesk, JetBrains Mono, Big Shoulders Display, Familjen Grotesk, Martian Mono, Geist, Geist Mono, Newsreader, Bricolage Grotesque, Atkinson Hyperlegible (Fontsource), and Inter, still pinned in make-motion but no longer usedOFL-1.1
ffmpeg (system binary, not vendored)LGPL/GPL
Kokoro TTS (kokoro-js, installed on demand)Apache-2.0

Not used, by design: Remotion and GSAP (free to use, but not under OSI licences).

09 What's inside

Skills

/make-dashboardPrompt → SQL + chart kinds → one self-contained HTML page with per-chart SQL provenance, themes, export and live reload. Offline; one-command refresh. Stamped with who made it.
/make-diagramPrompt → JSON topology → one self-contained HTML diagram (architecture, flowchart, sequence or state) laid out with ELK, with zoom, hover tracing, a story player, an Explore panel and full screen. Offline.
/make-explainerPrompt → typed JSON sections → one self-contained HTML page or slide deck: branch recap, concept explainer, plan, test checklist with saved progress, slides. Embeds diagrams, charts and video. Prose lint; offline.
/make-scenePrompt or your rows → scene JSON → one self-contained three.js page: your data as a 3D city, forest, galaxy or terrain, or a make-diagram diagram or make-dashboard chart lifted into 3D. Orbit, hover for exact values, a camera tour, full screen, PNG export; a table without WebGL. Offline.
/make-motionPrompt → scene spec → local preview with scrubber + deterministic 1080p MP4, silent unless asked; music from your file or a Kokoro voiceover. Targeted edits change one field.

Tooling

bin/install.mjsCross-host installer for Claude Code, Codex, GitHub Copilot and Gemini CLI.
scripts/doctor.mjsOne per skill: reports missing dependencies with a per-OS fix (--json, --for, --fix); installs only what you agree to.
bin/test/portability.test.mjsLints every SKILL.md for host-only tool names, hard-coded ~/.claude paths and bash-only commands.
eval/Cross-host eval: fixed prompts per skill through claude, codex, codex --oss, gemini and copilot; the 2026-10-10 full run covered dashboard and motion and skipped Gemini.

Explore the other plugins

Browse the full skills & tools catalog →