Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
It is built for developers and AI agents that need repeatable, diffable vector assets -- logos, social cards, diagrams -- without hand-editing SVG. Every scene is plain JSON checked against a schema that ships with the tool, and one scene renders to as many outputs, formats and light/dark variants as its config declares.
From PyPI:
uv tool install predraw
From npm (requires Python 3.11+):
npm i -g predraw
Create a project directory:
my-asset/
main.json
config.json
main.json -- the scene:
{
"width": 400,
"height": 200,
"styles": {
"bg": {"light": "#ffffff", "dark": "#1a1a1a"},
"fg": {"light": "#000000", "dark": "#ffffff"}
},
"elements": [
{"type": "background", "fill": "$bg"},
{
"type": "text",
"id": "title",
"content": "hello world",
"x": 200,
"y": 110,
"anchor": "middle",
"fill": "$fg",
"font": {"family": "Liberation Sans", "size": 48, "weight": 700}
}
],
"pipeline": [
{"action": "text-to-paths", "target": "title"}
]
}config.json -- output targets:
{
"outputs": [
{"format": "svg", "path": "hello-dark.svg", "mode": "dark"},
{"format": "png", "path": "hello-light.png", "mode": "light"},
{"format": "webp", "path": "hello-dark.webp", "mode": "dark", "quality": 90}
]
}Build:
predraw build my-asset/
Or auto-rebuild on changes:
predraw watch my-asset/
Create a starter project with main.json and config.json.
predraw init # current directory
predraw init my-asset/ # create in new directory
Render all outputs defined in config.json. Path can be a directory (looks for main.json) or a file.
predraw build # current directory
predraw build path/to/project # explicit path
predraw build --dry-run # preview without rendering
Auto-rebuild when project files change. Polls for changes every 0.5s with 0.3s debounce.
predraw watch # watch current directory
predraw watch my-asset/ # watch specific project
Convert a directory project into a single portable JSON file. Imports are resolved and inlined as defs.
predraw pack my-asset/ -o packed.json
Convert a packed JSON file back into a directory project with separate component files.
predraw unpack packed.json -o my-asset/
Validate a scene or config file against the JSON Schema. Auto-detects file type (presence of "outputs" key = config, otherwise = scene).
predraw validate main.json
predraw validate config.json --schema config
| Type | Purpose | Key properties |
|---|---|---|
background |
Full-canvas fill | fill |
rect |
Rectangle | x, y, width, height, fill, stroke |
path |
SVG path | d, fill, opacity, transform, stroke |
text |
Text (can convert to paths) | content, font, anchor, charStyles, stroke |
group |
Container | elements, transform |
All visible elements support stroke properties:
{"type": "rect", "width": 100, "height": 50, "stroke": "#ff0000", "strokeWidth": 2, "strokeDasharray": "5 3"}Properties: stroke, strokeWidth, strokeDasharray, strokeLinecap (butt/round/square), strokeLinejoin (miter/round/bevel), strokeOpacity.
Fill and stroke accept gradient objects:
{"fill": {"type": "linear-gradient", "angle": 90, "stops": [
{"offset": 0, "color": "#ff0000"},
{"offset": 1, "color": "#0000ff"}
]}}
{"fill": {"type": "radial-gradient", "cx": 0.5, "cy": 0.5, "r": 0.5, "stops": [
{"offset": 0, "color": "#ffffff"},
{"offset": 1, "color": "#000000", "opacity": 0.5}
]}}Reference a component from imports or defs:
{"use": "logo", "transform": {"translate": [100, 50], "scale": [2, 2]}}Property overrides cascade to children:
{"use": "logo", "fill": "#ff0000", "opacity": 0.5}Define color tokens with light/dark variants:
{
"styles": {
"primary": {"light": "#000000", "dark": "#ffffff"},
"muted": {"light": "#666666", "dark": "#999999"}
}
}Reference with $ prefix: "fill": "$primary". Mode is selected per-output in config.json.
Style individual characters differently:
{
"type": "text",
"content": "releasable",
"fill": "#ffffff",
"charStyles": [
{"chars": "rlsbl", "opacity": 1.0},
{"chars": "eeaa", "opacity": 0.12}
]
}Ordered postprocessing steps:
| Action | Fields | Description |
|---|---|---|
text-to-paths |
target |
Convert text to path outlines via font glyphs |
center |
target, axis (x/y/both) |
Center element on canvas |
place |
target, below/above/left/right, gap |
Position element relative to another |
group |
targets, id |
Wrap elements into a group |
Place .ttf or .otf files in a fonts/ directory in your project. These are searched before system fonts, enabling reproducible rendering across machines.
my-asset/
main.json
config.json
fonts/
Inter-Bold.ttf
Split reusable parts into separate files:
project/
main.json
config.json
components/
logo.json
main.json:
{
"imports": {"logo": "./components/logo.json"},
"elements": [
{"use": "logo", "transform": {"translate": [10, 10]}}
]
}predraw pack inlines imports as defs:
{
"defs": {
"logo": {"type": "group", "elements": [...]}
},
"elements": [
{"use": "logo"}
]
}config.json defines output targets:
{
"outputs": [
{"format": "svg", "path": "out.svg"},
{"format": "png", "path": "out-dark.png", "mode": "dark"},
{"format": "webp", "path": "out.webp", "mode": "dark", "quality": 90}
]
}| Field | Required | Description |
|---|---|---|
format |
Yes | svg, png, or webp |
path |
Yes | Output file path (relative to project dir) |
mode |
No | light or dark (default: dark) |
quality |
No | WebP quality 1-100 (default: 90) |
predraw documents are validated with strictspec.
The authoritative schemas are authored in strictspec's TOML surface syntax and generate
the runtime validators (strictspec gen, driven by strictspec.toml):
predraw/schema/scene.schema.toml-- scene formatpredraw/schema/config.schema.toml-- config format
Every scene and config document must carry a top-level integer format_version (currently
1); documents authored before this requirement are stamped once via
scripts/stamp_format_version.py. Validation is strict: unknown keys, ambiguous alias pairs
(a canonical spelling and its snake_case alias both present), and numerically unrepresentable
lexemes are hard errors.
- Python 3.11+
- System fonts for
text-to-paths(searches standard font directories)
MIT