Skip to main content

sprout-toolkit

PyPI version CI Pipeline License: MIT Python 3.10+

SPROUT TOOLKIT — Skia Procedural Rendering & Optimization Unified Toolkit

Grow your 2D assets procedurally.

Sprout is an open-source toolkit for deterministic procedural 2D asset generation targeting Expo + react-native-skia. It generates atlases, tilemaps, autotiles, bitmap fonts and SkSL shaders from JSON specs.

Hero blob walk cycle Spark burst Critter roster Flora roster Face moods Object grammar


Architecture

sprout/
├── sprout/                     # Python package
│   ├── __init__.py             #   __version__
│   ├── __main__.py             #   `python -m sprout` entry point (npm bin target)
│   ├── cli.py                  #   CLI (Typer): generate, batch, watch, info, lint, diff, validate, catalog, import
│   ├── exporter.py             #   Spritesheet packing, manifest.json, index.ts (+ tiers/silhouette)
│   ├── spec.py                 #   Load/validate JSON specs (schema v0)
│   ├── catalog.py              #   Catalog file -> spec codegen (external mapping)
│   ├── palettes.py             #   Named palettes + outline rule
│   ├── autotile.py             #   8-bit masks, 16/47 lookup
│   ├── sksl.py                 #   Runtime SkSL shaders (FBM + tier templates)
│   ├── assets/fonts/           #   Bundled font (ships in the wheel)
│   └── generators/             #   Generation plugins
│       ├── base.py             #     FrameData + Generator (contract)
│       ├── terrain.py          #     Seamless tiles + autotile
│       ├── props.py            #     Static objects (12 kinds incl. v3 grammar, anchors)
│       ├── particles.py        #     Animated bursts with easing
│       ├── ui.py               #     Button, slider, 9-patch panel
│       ├── font.py             #     Bitmap font from TTF (one glyph per frame)
│       ├── blob_walk.py        #     8-frame walk cycle
│       ├── critter.py          #     5 archetypes with composable parts
│       ├── flora.py            #     Canopies + undergrowth, sway anim
│       └── face.py             #     16 moods with blink
├── specs/                      # Example specs (one per generator)
├── tests/                      # 420 tests (determinism, generators, CLI, catalog, tiers)
├── docs/                       # spec.md, cli.md, api.md, generators.md + showcase/
├── npm/                        # npm wrapper (`npx sprout`, strict version pin)
├── site/                       # Landing page (GitHub Pages)
├── .github/workflows/          # tests.yml (matrix + wheel), ci.yml (pipeline), publish.yml, pages.yml
├── LICENSE                     # MIT
└── pyproject.toml

Features

Feature Description
Deterministic procgen Numeric seeds → reproducible assets (testable in CI).
8-bit auto-tile 8-bit autotile mask (N/E/S/W + diagonals), 16 or 47 variants.
Bitmap fonts from TTF Glyph atlas with advance metrics, ready for text layout.
Runtime SkSL shaders Generated Skia shaders (FBM value-noise, tileable), no bundle weight added.
Spec introspection sprout info/sprout lint/sprout diff to inspect, validate quality, and compare builds.
Catalog codegen sprout catalog + validate --coverage: specs from any catalog file via an external mapping.json.
Loose PNG import sprout import: pack hand-drawn/external frames into an atlas + manifest + index.ts.
npm wrapper npx sprout (Node ≥18) spawning the Python CLI with a strict version pin.
Flexible export PNG8/PNG24, TexturePacker format, atlas mipmaps — all opt-in.

Showcase

Everything below is generated from JSON specs — same seed, same pixels, every time.

Hero blob walk cycle, 8 frames Spark burst, 8 frames
blob_walk — 8-frame walk cycle (specs/demo.json) particles/spark — one-shot animated burst (specs/particles.json)

Autotile island, 47 variants

terrain + 47-variant autotile: island assembled from mask → frame lookups (specs/autotile.json, canonicalMask in the generated index.ts).

Props atlas: rock, bush, chest, mushroom, flower

props — 5 kinds × deterministic variants, each frame with an anchor point (specs/props.json).

Critter roster: 5 archetypes

critter — 5 archetypes (quadruped, bird, fish, reptile, bug) with composable parts + idle frames (critter_idle.gif) (specs/critter.json).

Flora roster

flora — canopies with sway (flora_sway.gif) (specs/flora.json).

Face moods

face — 16 moods with blink (specs/face.json).

Object grammar: 7 families, 27 forms

props v3 object grammar — 7 families × 27 forms for flavors + things (specs/objects.json).

UI atlas: button states, sliders, 9-patch panels

ui — button states, slider progress + knob, 9-patch panel (specs/ui.json).

SPROUT rendered with the generated bitmap font

font — bitmap font rasterized from the bundled TTF, with advance metrics for text layout (specs/font.json).

Installation

pip install sprout-toolkit
# or for development:
git clone https://github.com/Tzinny-dev/sprout-toolkit
cd sprout-toolkit
python -m venv .venv && source .venv/bin/activate
pip install -e ".[test]"

The installed command is sprout (not sprout-toolkit — that's just the PyPI distribution name).

npm wrapper (npx sprout)

npm install --save-dev sprout-toolkit
python3 -m pip install "sprout-toolkit==0.3.0"   # pinned CLI for the wrapper
npx sprout --version

The sprout bin (npm/bin/sprout.js) spawns python3 -m sprout and refuses to run when the installed CLI version differs from the wrapper's own — both channels release in lockstep (see CHANGELOG). Point it at a non-default interpreter with SPROUT_PYTHON=/path/to/python3.

Quick start

Generate assets

# New here? 10-minute walkthrough: docs/starter-tutorial.md
# Fastest start: writes starter.json + generates its atlas
sprout init --out ./assets/starter

# Generate every asset defined under specs/
sprout batch specs/ --out ./out

# Generate a specific spec
sprout generate specs/demo.json --out ./out

# Watch mode: regenerates when files under specs/ change (Ctrl-C to exit)
sprout watch specs/ --out ./out --interval 1.0

# Inspect a spec (human report or JSON)
sprout info specs/particles.json
sprout info --json specs/particles.json

# Analyze spec quality: atlas padding + empty frames
sprout lint specs/ui.json
sprout lint --json specs/ui.json

# Generate a bitmap font atlas (printable ASCII, bundled font)
sprout generate specs/font.json --out ./out

# Compare two specs, or two already-generated output directories
sprout diff specs/props.json specs/ui.json
sprout diff --json ./out/a ./out/b

# Validate a spec
sprout validate specs/demo.json

# Coverage: fail if any catalog id has no frame (CI check)
sprout validate specs/catalog.json --coverage catalog.ts --field key --map mapping.json

# Codegen: catalog file + external mapping.json -> spec
sprout catalog catalog.ts --field key --map mapping.json --out specs/catalog.json

# Pack loose PNG frames (hand-drawn/external art) into an atlas + manifest
sprout import ./frames/ --out ./assets/gestures --cols 2

# Multi-resolution tiers: one atlas per size + combined index.ts (atlasSources, pickTier)
sprout generate specs/demo.json --tiers 64,128,256 --out ./tiers

# Single-resolution override / prebaked silhouette mask (black x alpha)
sprout generate specs/demo.json --frame-px 128 --out ./out-128
sprout generate specs/demo.json --silhouette --out ./out

Export options (generate / batch)

# PNG8 (indexed palette, lighter atlas) or PNG24 (no alpha)
sprout generate specs/ui.json --png-mode png8
sprout generate specs/autotile.json --png-mode png24   # only if the atlas has no transparency

# In addition to manifest.json, emit <name>.tpsheet.json (TexturePacker JSON Hash format)
sprout generate specs/props.json --texturepacker

# Chain of atlas mip levels (@0.5x, @0.25x, @0.125x by default)
sprout generate specs/particles.json --mipmaps
sprout generate specs/particles.json --mipmaps --mipmap-levels 2

--png-mode defaults to rgba (no change). png8 quantizes to 256 colors while preserving the alpha channel (ideal for pixel-art-style limited palettes); png24 drops alpha entirely — only use it on atlases with no real transparency (e.g. terrain). --texturepacker/--mipmaps are opt-in and don't affect manifest.json/index.ts, except that --mipmaps adds the mipmaps.levels block to the manifest.

Available generators

generator Description Params
terrain Seamless noise tiles + 16/47 autotile contrast, lift, cells, octaves, bevel, autotile
blob_walk 8-frame walk cycle (hero blob) colors (body, outline, belly, ...)
props Static objects: rock, bush, chest, mushroom, flower — each frame includes an anchor point (frames[].anchor in the manifest, computed from the real alpha bbox) kind, fill, outline
particles Animated bursts: spark, smoke, dust, bubble kind, particles, core, trail
ui Interface: button (normal/hover/pressed states), slider (progress + knob), panel (9-patch, frames=9) kind, fill, outline, accent
font Bitmap font from TTF, one glyph per frame (frames = len(chars)) chars, font_path, size, fill

Example spec using props:

{
  "name": "props_atlas",
  "seed": 2024,
  "layout": { "framePx": 64, "cols": 6, "tileLogical": 32, "sample": "nearest" },
  "items": [
    { "id": "rock",  "generator": "props", "frames": 6, "params": { "kind": "rock" } },
    { "id": "chest", "generator": "props", "frames": 4, "params": { "kind": "chest", "fill": [160, 110, 50] } }
  ]
}

Development

pip install -e ".[test]"
pytest -q

CI (.github/workflows/tests.yml) runs the full suite on every push/PR, across Python 3.10–3.13; .github/workflows/ci.yml runs the pipeline checks (spec lint, end-to-end generation, npm wrapper version pin, and the catalog coverage example). Publishing to PyPI is manual (.github/workflows/publish.yml, trusted publishing via OIDC) — see CHANGELOG.

Releasing / Emergency Rollback

CI runs on every push/PR: tests.yml (Python 3.10–3.13 matrix + wheel smoke test) and ci.yml (spec lint, generation, npm pin). Publishing to PyPI is manual (.github/workflows/publish.yml, trusted publishing via OIDC) — see CHANGELOG.

Releasing (P1 checklist)

  1. Bump version in pyproject.toml, sprout/__init__.py and npm/package.json (all three are asserted equal by tests/test_fase8.py; publish.yml's check-version job also verifies the first two).
  2. Add a ## [<version>] section to CHANGELOG.md under [Unreleased].
  3. Tag and push: git tag v<version> && git push origin v<version> is not enough on its own — the publish workflow is workflow_dispatch only (never tag-triggered). Go to GitHub Actions → publish → "Run workflow" and pass the exact version string.
  4. Add a GitHub Release (auto-drafting from the tag is fine) with the CHANGELOG section as notes.
  5. Publish the npm wrapper from npm/ (npm publish) with the same version — the wrapper pins the CLI exactly.

Emergency rollback

PyPI versions are immutable — never delete, only yank:

  1. On https://pypi.org/manage/project/sprout-toolkit/release//, click Delete all files (irreversible) for small issues, or yank (keeps the version but blocks installs) for egregious bugs.
  2. Bump a patch (0.2.1 → 0.2.2) immediately with a fix; never re-use a yanked version number.
  3. If the GitHub Release asset was also bad, delete the tag + release and re-tag on the fixed commit.
  4. Post an incident note on the release you yanked linking to the replacement.

Credits

  • typer + Pillow — CLI and image generation
  • DejaVu Fonts (DejaVuSansMono-Bold.ttf, bundled in sprout/assets/fonts/) — Bitstream Vera license, see sprout/assets/fonts/DejaVuSansMono-Bold.LICENSE.txt

License

MIT — see LICENSE.

Metadata

Release files for sprout-toolkit 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sprout-toolkit 0.3.0
File Size Uploaded
sprout_toolkit-0.3.0.tar.gz 304.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sprout-toolkit 0.3.0
File Interpreter ABI Platform
sprout_toolkit-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 584.0 kB

Release files / sprout_toolkit-0.3.0.tar.gz

Download URL sprout_toolkit-0.3.0.tar.gz
Size 304.6 kB
Tags Source
SHA-256 checksum
How to use checksums
d9d217e2d56e7fd5e71824f4276eb3293e330bfb71a31372e506180fd217771d
BLAKE2b-256 checksum
How to use checksums
c04a31e30c01b8415f5f4bcfd22250ccf4bf9b08b32cf043710e718459267686
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / sprout_toolkit-0.3.0-py3-none-any.whl

Download URL sprout_toolkit-0.3.0-py3-none-any.whl
Size 279.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5a9ac3fa7afe9697402cfe01dce4641c4e5d5502a126eb996494a5edea8e45ac
BLAKE2b-256 checksum
How to use checksums
226159e2aba7e9d416e025b697ecc5d491266f0eb007f59f2d6ad5aa5b6d424c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.1

2 release files

0.4.0

2 release files

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page