toonstrip v0.1.9 · MIT · Astro integration

Comic strips from data, drawn live on your page.

Write panels, characters, and lines as JSON. toonstrip renders them on real canvases that reflow with the page and read aloud to screen readers. No image exports to keep in sync.

Open the playgroundRead the docs

1100 px

Drag the slider. The strip is not an image: each panel is a canvas, and the column count comes from the container width. Edit this strip in the playground →

30-second install

Three steps, one strip.

Install two packages, register the integration, drop the component in a page. The panel on the right is exactly what the snippet renders.

terminal
pnpm add @toonstrip/astro @toonstrip/pack-comic-chat
astro.config.mjs
// astro.config.mjs
import { defineConfig } from "astro/config";
import { toonstrip } from "@toonstrip/astro";

export default defineConfig({
  integrations: [toonstrip({ packs: ["@toonstrip/pack-comic-chat"] })],
});
src/pages/index.astro
---
import { ComicStrip } from "@toonstrip/astro";
---
<ComicStrip
  document={{
    version: 1,
    panels: [{
      backdrop: "field",
      bodies: [{ character: "dan", emotion: { angle: 0, intensity: 0.8 }, pose: "wave" }],
      speakers: [{ speaker: "dan", balloon: "speech", text: "Hello from toonstrip!" }],
    }],
  }}
  packs={["comic-chat"]}
  client:visible
/>

Result

How it works

You write data. A pack supplies the art. It renders live.

{ "backdrop": "field",
  "camera": "medium",
  "bodies": [{ "character": "dan",
               "emotion": { "angle": 0, "intensity": 0.8 } }],
  "speakers": [{ "speaker": "dan", "balloon": "speech",
                 "text": "Hello!" }] }

You write data

A Comic Strip Document: panels, who is on stage, what they feel, what they say. Plain JSON with a published schema. No TypeScript required to produce it.

A pack supplies the art

Characters, poses, expressions, and backdrops come from an asset pack. Fifteen characters and three backdrops ship by default; add your own with a sprite sheet and a JSON manifest.

canvasdevicePixelRatioResizeObserverrole="img"

It renders live

Each panel is its own canvas, sharp at any zoom, re-laid out when the container resizes, and described to assistive tech from the dialogue itself.

Use cases

Where a strip beats a paragraph.

Every strip below is hand-written JSON, rendered live. Open any of them in the playground.

TL;DR at the top of a long post

Three panels that say what four thousand words say. The post stays; the strip is the summary.

Open in playground →

Tutorials and FAQs as dialogue

A question and an answer read faster with two faces than with a definition list.

Open in playground →

Replaying a conversation

A chat thread or interview excerpt, with the room title as the channel name. Lines map straight to balloons.

Open in playground →

Data from anywhere

The document is JSON. Write it by hand, generate it from a script, store it in a CMS field, or have an LLM you call yourself produce it. toonstrip only renders.

Open in playground →

A static PNG when you need one

npm READMEs, emails, social cards, and OG images can't run a canvas. The CLI renders the same document to a PNG with the same layout and art, so one JSON file feeds both.

Open in playground →
sh
npx -p @toonstrip/cli toonstrip render strip.json --out strip.png --width 1200

Asset packs

Bring your own characters.

The default pack is the original Microsoft Comic Chat roster, redistributed under MIT. The second pack is one original character and one backdrop: the smallest proof that the pack contract is not tied to the default art.

comic-chat · 15 characters · 3 backdrops

dan
anna
bolo
denise
kevin
lynnea
margaret
rebecca
tiki
sage
kwensa
hugh
maynard
scotty
tongtyed

example · 1 character · 1 backdrop

pip

How to add a character

Why not an image?

What a live canvas gives you that a PNG can't.

PropertyPre-rendered PNGExported SVGtoonstrip
Reflows to the containerNo. One fixed layout.Scales, doesn't reflow.Column count follows width.
Sharp on any displayOnly at export size.Yes.Yes, DPR-aware backing store.
Screen readersOne alt text, if you wrote it.Depends on the export.Every panel described from its own lines.
Change one line of dialogueRe-render, re-upload.Re-export.Edit the JSON.
Page weight for a 3-panel stripOne large image.Text-heavy vector.Only the sprites it references.

Where it came from

Extracted from a bigger, weirder project.

toonstrip started as the comic-rendering half of Grues in Comic, a project that plays classic Zork-style interactive fiction and renders the playthrough live as a Comic Chat-style strip. Panels, poses, and speech balloons are driven by the original Comic Chat engine's own text-to-affect logic, ported as-is with no runtime LLM involved.

That rendering engine (panels, characters, poses, emotions, balloons, backdrops) turned out to be useful on its own, with no game and no Zork coupling attached. toonstrip is that half, pulled out and generalized: same canvas renderer and the same default Comic Chat asset pack, but driven by plain data you supply yourself.

Grues in Comic: a two-panel Comic Chat-style strip of a Zork scene above the game's text console.
Grues in Comic, rendering a Zork playthrough with the same engine.

FAQ

Short answers.

Does toonstrip generate the comic for me?

No. toonstrip only renders the JSON document you give it — the dialogue, emotions, and direction are yours, your script's, or an LLM you call yourself.

Does it render on the server?

The Astro component emits an inert tag on the server and the browser draws. For static PNGs, the CLI runs the same renderer in Node.

Can I use it commercially?

Yes. The code is MIT, and the default pack's Comic Chat art is redistributed under MIT terms.

How big is it?

About 36 KB gzipped for element + core + schema validator; sprite sheets load per character actually used.

Read the full FAQ →