cd /news/developer-tools/i-built-an-agent-skill-that-turns-yo… · home topics developer-tools article
[ARTICLE · art-109477] src=github.com ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

I built an agent-skill that turns your texts into diagrams and BI-visuals

Developer imshaikot released render-visual-skill, an agent skill that lets coding agents generate diagrams, presentation slides, social cards, code snippets, and device mockups as HTML/SVG rendered to PNGs via headless Chromium, with no design tool, API, or npm dependencies. The skill supports sequence diagrams animated into GIFs in pure Node, includes 57 referenceable parts and eight themes, and works with Claude Code, Cursor, GitHub Copilot, VS Code, Codex, Gemini CLI, OpenCode, Amp, and Goose. It is available via the Claude plugin marketplace command 'claude plugin marketplace add imshaikot/render-visual-skill'.

read11 min views2 publishedAug 25, 2026
I built an agent-skill that turns your texts into diagrams and BI-visuals
Image: Michielbdejong (auto-discovered)

Your coding agent cannot draw. This Agent Skill fixes that — diagrams, presentation slides, social cards, code snippets and device mockups, authored as HTML/SVG and rendered to crisp PNGs by the headless Chromium you already have. No design tool, no API, no npm dependencies.

diagram · ember | slide · slate | card · paper |

Sequence diagrams animate — each step tweened over several frames, assembled into a looping GIF in pure Node. Chrome renders the frames in parallel, the built-in zlib decodes them, and a hand-rolled GIF89a/LZW encoder with changed-region deltas does the rest:

claude plugin marketplace add imshaikot/render-visual-skill
claude plugin install render-visual-skill@render-visual-skill

Works in any skills-compatible agent — Claude Code, Cursor, GitHub Copilot / VS Code, Codex, Gemini CLI, OpenCode, Amp and Goose. Install covers the clone-a-branch lane that every agent other than Claude Code uses.

Every image in this README was rendered by the skill.

GalleryCapabilitiesRequirementsInstallUsageThemesElement libraryCLI referenceWhat's insideWhy HTML instead of a design toolUninstallLicense

Five more diagram idioms ship as templates, each 1360×740 and assembled from the same parts:

swimlane · ember | cluster · slate | deployment · blueprint | tree · paper | mind map · neon |

Same markup, different theme:

Code snippets get a Carbon-style window — hand-highlighted with a fixed token→accent mapping, line numbers, a highlight line and diff rows, in any of the eight themes:

Diagrams | Architecture and flow figures — nodes, labelled arrows, return paths, bridged line crossings. 1360×740 | Presentation slides | Kicker, gradient headline, up to three points, footer. 1920×1080; a deck is one file per slide | Social / og cards | Mark, headline, one-paragraph pitch, chips. 1200×630 | Code snippets | Carbon-style window, hand-highlighted against a fixed token→accent mapping, line numbers, a highlight line, diff rows | Sequence diagrams | Lifelines, calls, returns, activations — rendered static, or animated step by step | Swimlanes | Lanes that own the steps, labelled handoffs, an exception path that stays in its lane | Trees | One hierarchy two ways — an indented tree view beside the same tree drawn node-link | Clusters | A dashed boundary, control plane, worker nodes and pods; what is outside it survives a rebuild | Deployment diagrams | 3-D nodes, «stereotypes» and artifact chips: which file lands on which machine, over which protocol | Mind maps | A question in the middle, branches around it, leaves as evidence | Animated GIFs | Steps tweened over several frames (slide , fade , pop ), assembled in pure Node. No ffmpeg | Element library | 57 referenceable parts — device frames, infrastructure shapes, a 3-D deployment node, charts, BI furniture and 30 glyphs | Chart & BI schematics | Pie, donut, bar, hbar, line, area, stacked, scatter, funnel, gauge, heatmap, sparkline, dashboard, KPI tile, table | Your own images | Screenshots and photos placed into a device frame or cropped to a shape, inlined before the render | Eight themes | Swap with one flag — every template consumes design tokens, never hard-coded colour | Alpha grades | Every colour token has a component twin, so oklch(var(--a1-raw) / 12%) gives any transparency of any accent — washes, edges and scrims that cannot drift from the colour they came from | Transparent output | A real alpha channel via --transparent , so a figure drops onto any background | Parallel-safe | Each render claims its own Chrome profile by pid lockfile, and reaps orphans an interrupted run left behind | Fails loudly | A wrong image at exit 0 is the one thing refused outright — blank canvases, missing stylesheets, unreadable images and unknown parts all fail, never render quietly wrong |

**Node 18+**A Chromium-based browser— Chrome, Chromium, Brave or Edge. Standard install paths andPATH

are probed;CHROME_PATH

overrides. Without one:npx @puppeteer/browsers install chrome@stable

(no root needed).Network access at render time, for theme fonts. All eight themes@import

fromfonts.googleapis.com

. Offline renders still succeed but fall back to system fonts, so they will not match the previews above.

It needs a shell and a local browser, so it cannot run on surfaces that have neither — claude.ai chat, the Skills API, cloud sessions and most CI images.

Claude Code — as a plugin (gets you /plugin update

). From your shell:

claude plugin marketplace add imshaikot/render-visual-skill
claude plugin install render-visual-skill@render-visual-skill

Or from inside Claude Code, as two separate commands — run the first, let it finish, then run the second:

/plugin marketplace add imshaikot/render-visual-skill
/plugin install render-visual-skill@render-visual-skill

Important

/plugin marketplace add

may open an Add Marketplace dialog. Only imshaikot/render-visual-skill

belongs in that field. Pasting both lines into it is rejected as an invalid owner/repo

shorthand — the /plugin install

line is a second command, not part of the source.

The repeated name is not a typo: render-visual-skill@render-visual-skill

reads as plugin@marketplace

, and here both are called the same thing.

Any agent — clone the skill into its skills directory. The skill

branch is published by CI with the skill at its root, so the clone target is the skill:

git clone --depth 1 -b skill https://github.com/imshaikot/render-visual-skill.git \
  ~/.agents/skills/render-visual

git clone --depth 1 -b skill https://github.com/imshaikot/render-visual-skill.git \
  ~/.claude/skills/render-visual

Update with git -C <that directory> pull --ff-only

.

Note

Cursor, VS Code, OpenCode, Amp and Goose read both ~/.agents/skills

and ~/.claude/skills

. Installing into both shows a duplicate entry in those five — pick one, or use the plugin lane for Claude Code and ~/.agents/skills

for everything else.

Just ask for a visual:

"make a diagram of our auth flow""turn these notes into a 6-slide deck, paper theme""an og card for this repo"**"put this screenshot in a browser frame"

Or drive the renderer by hand:

S=~/.agents/skills/render-visual
node $S/scripts/render.mjs  $S/templates/diagram.html  figure.png   --theme slate
node $S/scripts/render.mjs  $S/templates/code.html     snippet.png  --theme paper --transparent
node $S/scripts/animate.mjs $S/templates/sequence.html sequence.gif --theme ember

The canvas size comes from the template's <body>

; --scale

defaults to 2 (retina). --theme

inlines the theme, so no themes/

directory has to sit beside your figure.

When a request names no theme, the agent asks rather than guesses — and remembers the answer. Standing choices (theme, where finished images land, scale) live in a .render-visual.json

at your project root, written only with your consent, so the next render doesn't re-open settled questions:

{ "theme": "slate", "output": "docs/figures/", "scale": 2 }

Anything said in the prompt beats the file; delete it to change course. The renderer itself never reads it — --theme

stays explicit on every command.

Every template consumes tokens only, so one source file renders in any theme.

Theme Mood Fonts
ember
Warm dark — amber-hued neutrals, cyan/ember/magenta accents Inter Tight + JetBrains Mono
slate
Cool dark — violet-leaning neutrals, jewel accents Space Grotesk + IBM Plex Mono
paper
Light editorial — warm paper, serif display, print restraint Fraunces + IBM Plex Mono
terminal
Near-black phosphor — mono everything, green/amber JetBrains Mono
blueprint
Drafting board — cyanotype navy, chalk lines, a grid that reads Archivo + Roboto Mono
frost
Light UI — cool white, glass surfaces, indigo/teal Manrope + JetBrains Mono
neon
After hours — indigo dark, high-chroma magenta and cyan Chakra Petch + Fira Code
sepia
Aged press — cream stock, brown ink, typewriter mono Newsreader + Courier Prime

templates/palette.html

is a specimen sheet that renders in whichever theme you hand it and labels itself from the tokens it was given — both fonts, the gradient, the neutrals, the alpha ladders, and the same parts dressed by that theme:

node $S/scripts/render.mjs $S/templates/palette.html palette.png --theme neon

blueprint | frost | neon | sepia |

Every colour token ships a component twin — --a1-raw

--a4-raw

, --ink-raw

, --ground-raw

, --surface-raw

— three bare OKLCH numbers, so any transparency of any token is one expression away:

.badge { background: oklch(var(--a1-raw) / 12%); border: 1px solid oklch(var(--a1-raw) / 45%); color: var(--a1); }
.scrim { background: oklch(var(--ground-raw) / 72%); }   /* a caption band over a photo */

The solid token is built from the same components (--a1: oklch(var(--a1-raw))

), so a wash can never drift from the colour it is a wash of — and an invariant refuses any theme whose component tokens are not composable, because a bad one paints nothing rather than failing.

Adding a theme is one CSS file defining the same tokens. Adding a template is one HTML file that consumes only tokens.

Figures assemble from 57 parts — window/browser/terminal/phone frames, database, server, queue, cloud, router, actor, shield, a 3-D deployment cube, a 30-glyph icon set, and the chart vocabulary below. A figure references a part rather than carrying a copy of its geometry:

<g data-part="el-database" data-accent="2" transform="translate(70,452)"/>

Every part is built from theme tokens, so it restyles with the theme like everything else:

The standard chart vocabulary — pie, donut, bar, line, area, stacked, scatter, funnel, gauge, heatmap, sparkline — plus BI furniture: a dashboard window, KPI tiles and a data table. Each spends a single accent, graded by opacity where categories must read apart, so a chart sits in a figure without competing with the arrows around it.

These are schematics of charts, not charts: every proportion in them is fixed and arbitrary, so they can say "a dashboard goes here" without pretending to be data. Plot real numbers with a real charting library.

Point data-image

at a screenshot or a photo on disk and it lands in a device frame — cropped to the shell's own corners, skeleton bars covered — or in any shape you ask for:

<g data-part="el-browser" data-image="./shot.png" data-align="top"/>
<image data-image="./avatar.jpg" data-shape="circle" x="60" y="420" width="160" height="160"/>

The bytes are read, format-checked and inlined before Chrome launches, so nothing is left for the browser to fetch and quietly fail at: a missing file, a .png

that is really a text file, a HEIC, or a remote URL is a fatal error naming the path — never an invisible hole in a figure that still screenshots as a success.

node $S/scripts/render.mjs <input.html> <output.png> [flags]
Flag Default
--theme
the page's own ember · slate · paper · terminal · blueprint · frost · neon · sepia ; inlined into a temp copy
--scale
2
Output multiplier — 1360×740 at 2× is a 2720×1480 PNG
--size
the <body>
WxH override, e.g. 1200x630
--transparent
off Real alpha channel: ground and furniture stripped
node $S/scripts/animate.mjs <input.html> <output.gif> [flags]
Flag Default
--fx
slide
Reveal preset: slide , fade , pop
--fps
25
Tween frame rate; 20/25/50 play back exactly
--transition
450
Milliseconds of tween per step
--delay
900
Milliseconds of dwell on each completed step
--hold
2600
Milliseconds on the final frame before looping
--jobs
4
Parallel Chrome instances
--scale
1
GIFs get heavy fast — stay at 1×
--keep-frames
off Keep the per-frame PNGs for inspection

--theme

and --size

work as in render.mjs

.

Renders are safe to run in parallel — each claims its own Chrome profile via a pid lockfile, and every run first reaps Chromes an interrupted run left holding a slot. Two scripts back that up:

node $S/scripts/doctor.mjs --prune   # reap orphans, clear stale locks, reclaim profile disk
node $S/scripts/selftest.mjs         # ~2m: assert all 22 concurrency and output invariants

Reach for doctor.mjs

when a render fails with "is another instance using profile".

skills/render-visual/       the skill — this directory is what gets installed
  SKILL.md                  workflow, aesthetic rules, layout discipline
  templates/                diagram · swimlane · tree · cluster · deployment ·
                            mindmap · sequence (animatable) · code — all 1360×740
                            slide 1920×1080 · card 1200×630
                            elements + charts (parts sheets) · palette (theme specimen)
  parts/                    57 includable elements — frames, shapes, charts,
                            BI furniture, glyphs
  themes/                   ember · slate · paper · terminal · blueprint · frost
                            neon · sepia  (design tokens, swappable)
  scripts/                  render.mjs (PNG) · animate.mjs (GIF) · gif.mjs (GIF89a encoder)
                            chrome.mjs (profiles, reaping, guards) · cli.mjs · doctor.mjs
                            parts.mjs (element includes) · images.mjs (image includes)
                            markup.mjs · selftest.mjs
.claude-plugin/             Claude Code plugin + marketplace manifests
previews/                   the images above

Versioned and diffable— a figure is a text file; regenerating after a copy change is one command** Consistent by construction**— templates consume theme tokens, so nothing is hand-picked per image** Agent-friendly**— an agent writes HTML far better than it steers a canvas

The skill keeps warm Chrome profiles in your temp directory, so clean those up before removing it:

node <skill directory>/scripts/doctor.mjs --prune   # reclaim profile disk, clear locks
rm -rf <skill directory>                            # e.g. ~/.agents/skills/render-visual

For the plugin lane, /plugin uninstall render-visual-skill

leaves its cache behind:

rm -rf ~/.claude/plugins/cache/render-visual-skill \
       ~/.claude/plugins/marketplaces/render-visual-skill

Nothing else is left: all temp state lives under $TMPDIR/render-visual/

, and renders never write into the directory they render from.

── more in #developer-tools 4 stories · sorted by recency
── more on @imshaikot 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-built-an-agent-ski…] indexed:0 read:11min 2026-08-25 ·