Integrate
Asset packs
Using the two shipped packs, registering more than one, and adding a character of your own.
An asset pack supplies the art a document refers to by id: character sprite sheets with a pose table, and backdrop images. The renderer never ships art of its own.
The two shipped packs
| Pack | npm | name | Contents |
|---|---|---|---|
| Comic Chat | @toonstrip/pack-comic-chat |
comic-chat |
15 characters, 3 backdrops. The original Microsoft Comic Chat art, MIT. |
| Example | @toonstrip/pack-example |
example |
1 character (pip), 1 backdrop (meadow). Original, tiny, and the template for your own. |
{
"version": 1,
"panels": [
{
"backdrop": "meadow",
"camera": "medium",
"bodies": [
{
"character": "pip",
"emotion": {
"angle": 0,
"intensity": 0.5
},
"pose": "wave"
}
],
"speakers": [
{
"speaker": "pip",
"balloon": "speech",
"text": "Hi! I'm from the example pack."
}
]
},
{
"backdrop": "meadow",
"camera": "medium",
"bodies": [
{
"character": "pip",
"emotion": {
"angle": 0,
"intensity": 0.2
},
"pose": "shrug"
}
],
"speakers": [
{
"speaker": "pip",
"balloon": "speech",
"text": "One character, one backdrop, one manifest."
}
]
}
]
}Registering more than one
List every pack in the integration and name the ones a strip needs in the component. Ids resolve across all registered packs; a character or backdrop id declared by two packs is a load-time error, not a silent first-match.
// astro.config.mjs
toonstrip({ packs: ["@toonstrip/pack-comic-chat", "@toonstrip/pack-example"] })
<ComicStrip document={doc} packs={["comic-chat", "example"]} client:visible />
{
"version": 1,
"panels": [
{
"backdrop": "meadow",
"camera": "medium",
"bodies": [
{
"character": "pip",
"emotion": {
"angle": 0,
"intensity": 0.6
},
"pose": "wave"
},
{
"character": "anna",
"emotion": {
"angle": 0,
"intensity": 0.6
}
}
],
"speakers": [
{
"speaker": "pip",
"balloon": "speech",
"text": "Two packs, one panel."
},
{
"speaker": "anna",
"balloon": "speech",
"text": "Ids just have to be unique across them."
}
]
}
]
}Pack layout
my-pack/
pack.json # name, version, license, characters[], backdrops[]
characters/<id>/sheet.png # one sprite sheet per character
characters/<id>/<id>.json # that character's parts: faces, torsos, rects, sockets
backdrops/<id>.png
NOTICE # required if any art is not wholly yours
pack.json:
{
"name": "my-pack",
"version": "0.1.0",
"license": "MIT",
"characters": [
{ "id": "pip", "name": "Pip", "manifest": "characters/pip/pip.json", "sheet": "characters/pip/sheet.png" }
],
"backdrops": [
{ "id": "meadow", "file": "backdrops/meadow.png", "width": 240, "height": 240 }
]
}
name is what strips reference, not the npm package name. license is required on every
pack.
A character manifest
A character is parts, not finished drawings: faces and torsos as rectangles in the sheet, each tagged with an emotion point, plus the neck socket so a face sits on any torso.
interface PackedAvatar {
name: string;
type: string;
torsoFirst: boolean; // torso draws over the collar, or under it
sheet: string; // path relative to this manifest
sheetWidth: number;
sheetHeight: number;
faces: PackedPart[];
torsos: PackedPart[];
bodies: PackedPart[]; // unused today; leave empty
}
interface PackedPart {
kind: "face" | "torso" | "body";
index: number;
emotion: string; // "NEUTRAL", "HAPPY", … or a gesture name
angle: number; // wheel angle, or 1001 wave / 1002 point / 1005 shrug
intensity: number;
cx: number; cy: number; // neck socket
cxDelta: number; cyDelta: number;
x: number; // where a balloon tail attaches
figure: { x: number; y: number; width: number; height: number };
aura: { x: number; y: number; width: number; height: number } | null;
}
Minimum: one face with emotion: "NEUTRAL" and one torso. Gestures are torsos at the
sentinel angles (1001 wave, 1002 point, 1005 shrug); support the ones you draw.
Validate
npx -p @toonstrip/cli toonstrip pack validate ./my-pack
Checks pack.json, every declared file, the NEUTRAL face, and that each backdrop’s real
pixel size matches what pack.json claims. It lists every problem, not just the first.
Warning
A character with faces but no torsos validates today and then renders nothing. Always declare at least one torso.
Adding a character, step by step
- Draw the parts on one sheet (any editor, or a script). At minimum a NEUTRAL face and a torso, each a clean rectangle.
- Write the manifest: copy
pip.jsonfrom the example pack and replace the rectangles and sockets with yours. - Add the character to
pack.json. - Run
pack validate. - Render one panel with it in the playground by serving your pack
locally, or in a Node script through
@toonstrip/core’srenderPanel.
The example pack’s sheet is drawn by a 60-line script with @napi-rs/canvas; it is the
smallest complete pack and the one to copy.
Tip
Doing this from real drawings rather than rectangles? Don’t write the manifest by hand:
@toonstrip/asset-packager takes a folder of per-pose PNGs, or one
labelled sheet, and writes pack.json, the sprite sheets and the sockets for you.
Licensing
Say what the art is. If any of it is not wholly yours, ship a NOTICE crediting the
source and its licence. The default pack’s NOTICE (Microsoft Comic Chat art, MIT, with a
trademark disclaimer) is the worked example.