Skip to main content

A playful Python toolkit for creative coding and small games.

Project description

Gummy Snake

PyPI Python Versions License: LGPL-2.1 Downloads

Gummy Snake is a playful Python toolkit for creative coding and small games. It is for people who want to sketch with code: draw shapes, animate motion, react to input, load sprites, play with pixels, and build visual toys without first building a full app.

The public API is Python-first. Function names use snake_case, sketches are ordinary Python files, and drawing, export, pixels, text, images, and native interactive windows are powered by the packaged Rust gummy_canvas runtime. On desktop builds, native windows and input use the SDL3-backed runtime. The Rust canvas owns the hot renderer state used to construct draw commands, including the current style, transform stack, image/text state, and GPU command batches. It also owns the mutable sketch context state for canvas lifecycle fields, timing, loop/redraw flags, input snapshots, and in-progress shape buffers.

Install

pip install gummy-snake

Published wheels include the required Rust gummy_canvas canvas runtime. Source or editable installs must build that PyO3 module; there is no Python renderer fallback for canvas-owned behavior. Local development builds compile SDL3 from source/static through Rust when native interactive support is enabled, so no separate system SDL3 install is normally required.

Install optional media helpers when you need camera or video frame sources:

pip install "gummy-snake[media]"

First Sketch

Create a file named circle_sketch.py:

import gummysnake as gs


@gs.setup
def setup() -> None:
    gs.create_canvas(400, 300)
    gs.no_stroke()


@gs.draw
def draw() -> None:
    gs.background(245)
    gs.fill(255, 90, 90)
    gs.circle(200, 150, 100)


gs.run()

Run it:

python circle_sketch.py

For repeatable scripts, use a bounded headless render:

gs.run(headless=True, max_frames=1)

Callbacks can also be async def, which is useful with async-compatible asset helpers:

image = None


@gs.preload
async def preload() -> None:
    global image
    image = await gs.load_image_async("sprite.png")

What You Can Make

  • 2D drawings with shapes, curves, color, transforms, and blend modes.
  • Animated sketches using the familiar setup() and draw() lifecycle.
  • Decorator-based sketches, async-compatible callbacks, and object-oriented Sketch subclasses.
  • Image and pixel experiments, including canvas export.
  • Text, font measurement, and accessibility descriptions.
  • Interactive sketches with SDL3-backed native windows, mouse, keyboard, and touch state when native window support is available.
  • WEBGL-style 3D sketches with primitives, lights, materials, models, textures, and shader objects. Built-in model and primitive draws use retained Rust/GPU buffers, GPU transforms/projection/depth, and built-in material shaders when GPU drawing is available.
  • Dense 2D scenes that rely on internal mixed primitive, line, sprite, and text batching rather than one Python-to-Rust call per draw.
  • Small games and visual toys using the examples as starting points.

Loaded images, models/meshes, and sounds keep Rust-managed asset handles behind friendly Python wrappers. This is intentional for performance: bulk asset bytes, geometry arrays, parsing, export, and metadata extraction should stay in the Rust canvas runtime so sketches avoid repeated Python object materialization and per-element loops. Normal load_image(); image(...) sprite drawing can stay on the fast renderer path, model export can use Rust-owned geometry without first creating Python Vec3 objects, and built-in WEBGL model draws can reuse retained GPU vertex/index buffers while the GPU handles transform, projection, depth testing, texture sampling, and material lighting. Loaded sounds keep their bytes and duration metadata in CanvasSound until user code asks for Python bytes. Image-local resize, mask, filter, crop/copy, and alpha compositing delegate bulk byte work to the Rust canvas runtime while keeping the Python Image API and version semantics. For pixel effects, load_pixels() returns gummysnake.core.pixels.PixelBuffer, a mutable list-like RGBA byte buffer that tracks dirty regions; load_pixel_bytes() provides a bytes readback path; and update_pixels() accepts lists, PixelBuffer, and buffer-like inputs such as bytes, bytearray, and memoryview. Buffer-like uploads use the Rust canvas buffer-protocol path without an intermediate Python bytes(...) copy, exact no-op byte uploads are skipped, and dirty row-aligned PixelBuffer changes can upload as smaller Rust regions. Small canvas get() and set() region operations use Rust region calls instead of reconstructing the full canvas as a Python image. For dense drawing loops, gs.fast() returns a frame-local facade that keeps public style/transform state while reducing global-mode dispatch overhead. Fill-only rectangles, triangles, circles, axis-aligned ellipses, compatible line runs, and mixed stroked/fill primitive groups can batch into compact Rust commands with per-record style and transform data. Supported primitive batches use procedural GPU instance paths where possible; static unchanged command streams can be retained and reused; unsupported transforms fall back to the general vertex path without changing public API behavior. Sprite-heavy loops can batch through the Rust image path with per-sprite transforms, source rectangles, tint, sampling, and blend state, including an internal atlas path for ordered draws from a small texture set. Text-heavy overlays can use text_batch() and text_widths() to submit many labels or measurements with fewer Python calls while staying on the Rust-owned text path. The renderer keeps text_width(), ascent/descent, and text_bounds() consistent with the current style used for drawing, and it mixes GPU glyph-atlas text with batched cached line-texture atlas fallback internally when that is needed to preserve ordered output around intervening shapes or images. Opt-in enable_performance_diagnostics() counters can identify readback, pixel conversion, upload, direct model/shape draw, primitive/image batch size and flush shape, GPU vertex-buffer, texture cache, GPU blend/region-effect passes, glyphon-backed text drawing, cached-text atlas fallback, and CPU compositing fallback paths. HiDPI/Retina rendering keeps sketch coordinates logical while physical pixel buffers and GPU vertices are scaled by pixel_density().

Learn More

For Contributors

This repository uses uv for Python commands:

uv sync --dev
uv run ruff check .
uv run mypy src
uv run pytest
uv run python scripts/source_size_audit.py
uv run python scripts/structure_audit.py

The canvas runtime is a required PyO3 module for development/source installs:

uvx maturin develop --manifest-path crates/gummy_canvas/Cargo.toml --features extension-module

The refactored Python package is split by responsibility: user-facing wrapper functions live in topic modules under src/gummysnake/api/ (for example lifecycle.py, input.py, images.py, pixels.py, text.py, models.py, shaders.py, sound.py, media.py, and three_d.py), SketchContext mixins live in src/gummysnake/context_mixins/, lifecycle code and object-mode facade groups live in src/gummysnake/sketch/, enum-backed constants live in src/gummysnake/constants/, and thin canvas backend/renderer facades delegate to src/gummysnake/backend/canvas_runtime/host/ and src/gummysnake/backend/canvas_runtime/renderer/. The renderer internals are grouped around bridge calls, lifecycle, counters, caches, payload builders, and primitive/image/text/pixel drawing. Shared test fakes live in tests/helpers/, fixtures live in tests/fixtures/, and generated example output stays under the gitignored examples/output/ tree. The native desktop runtime itself lives in crates/gummy_canvas, owns sketch context state, canvas draw state, command construction, cache/dirty-state helpers, and GPU render-pass batching, and uses SDL3 for windowing, resizing, and input event collection. Python keeps the public API, callbacks, plugin hooks, and friendly wrapper objects.

The contributor documentation explains the architecture, lifecycle, testing workflow, and release shape in more detail:

Performance benchmarks are opt-in:

uv run pytest tests/benchmark/test_canvas_backend_perf.py --run-benchmarks
uv run pytest tests/benchmark/test_api_overhead_perf.py --run-benchmarks
uv run pytest tests/benchmark/test_image_pipeline_perf.py --run-benchmarks
uv run pytest tests/benchmark/test_model_export_perf.py --run-benchmarks
uv run pytest tests/benchmark/test_webgl_3d_perf.py --run-benchmarks

Canvas backend benchmark scenarios measure native interactive presentation and are expected to average at least 240 FPS. Headless/offscreen numbers are useful for export diagnostics, but they are not the runtime performance acceptance metric. The canvas benchmark payload includes renderer metrics for draw counts, primitive/image batch records, flushes, largest coalesced batch size, vertex uploads, texture uploads/reuse, text cache hits, pixel readbacks/uploads, GPU region effects, and presented/rendered frame counts. WEBGL frame-style benchmark scenarios use the same FPS floor. High-count primitive and sprite stress variants keep explicit 60 FPS gates for 10k stress scenes, and the high-count primitive gate covers 10k, 50k, and 100k static retained-batch scenes behind --run-high-count-benchmarks. Model export benchmarks use a memory budget for streaming OBJ/STL output. Machine-specific baseline snapshots live in tests/benchmark/baselines/; keep captured values as measured and note both the required 240 FPS floor and the higher recovered-variant margin target when applicable.

Long-running resource lifecycle checks are also opt-in:

uv run pytest tests/stress --run-stress -q -s

Compatibility

Gummy Snake keeps the sketch lifecycle familiar, but it is not a browser port. It does not include DOM helpers, browser-only APIs, JavaScript aliases, or a Pillow/Pyglet/Python renderer fallback. Unsupported features raise explicit package errors so sketches fail clearly.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gummy_snake-0.7.1.tar.gz (315.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

gummy_snake-0.7.1-cp312-cp312-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.12Windows x86-64

gummy_snake-0.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

gummy_snake-0.7.1-cp312-cp312-macosx_11_0_arm64.whl (4.8 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

gummy_snake-0.7.1-cp312-cp312-macosx_10_12_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

File details

Details for the file gummy_snake-0.7.1.tar.gz.

File metadata

  • Download URL: gummy_snake-0.7.1.tar.gz
  • Upload date:
  • Size: 315.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gummy_snake-0.7.1.tar.gz
Algorithm Hash digest
SHA256 720744f1b15c9dce35445b0dc6c3e06276d12f9f8b769d7e6e975dbb41e22b46
MD5 8e44ae5d7a294a93b58d65f708986936
BLAKE2b-256 4ab895ea9ea9777b6c1a562cb58bd3ff9d8220207c982788ba144908cd0c1638

See more details on using hashes here.

Provenance

The following attestation bundles were made for gummy_snake-0.7.1.tar.gz:

Publisher: publish.yml on JonathanHHenson/gummy_snake

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gummy_snake-0.7.1-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for gummy_snake-0.7.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 c31c05941f414bb32d463da0102fc3ad9fc735523f761752c1a0d2365068a8df
MD5 2a8ebd7a97962304ddd6db26544370fe
BLAKE2b-256 209010b0c13316e85584eccc80025471032d7ffbfa710fb67edd7cb623c4b594

See more details on using hashes here.

Provenance

The following attestation bundles were made for gummy_snake-0.7.1-cp312-cp312-win_amd64.whl:

Publisher: publish.yml on JonathanHHenson/gummy_snake

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gummy_snake-0.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gummy_snake-0.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7e950c6dc00369a74debc1c329e2edf61fb9723c5a1da215aa0f72cb2c0d46e1
MD5 d1833637e178ba53e6eb39b370b6794e
BLAKE2b-256 59bbf1b4fa642e96711527e92443431c4a342da1552de9b3b81d7ec5eccd5c14

See more details on using hashes here.

Provenance

The following attestation bundles were made for gummy_snake-0.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on JonathanHHenson/gummy_snake

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gummy_snake-0.7.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gummy_snake-0.7.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e1c4fd330700a9e33ebb27a75762e7f09d4e3e42f165bc19f4fe9b6eb6cd154c
MD5 ff48e7f77ef1041b8718e2b5abacd1cc
BLAKE2b-256 ee82923e0a0aec706da50d8086fee60f1de8e6af624be7896ee0a61961bd592f

See more details on using hashes here.

Provenance

The following attestation bundles were made for gummy_snake-0.7.1-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: publish.yml on JonathanHHenson/gummy_snake

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gummy_snake-0.7.1-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for gummy_snake-0.7.1-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9d35a83a32abf4ce76529b106ffcd903af072d62fbc1b5f335f83eee5de05c16
MD5 f69b78aa2431434fb0eacfda2daea7ec
BLAKE2b-256 913f87fc4e9cb78a4a7bc68f06cdd72371d03929ba58523ecbe3faec9610e3a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for gummy_snake-0.7.1-cp312-cp312-macosx_10_12_x86_64.whl:

Publisher: publish.yml on JonathanHHenson/gummy_snake

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page