Skip to main content

Kiln

CI Python 3.11+ macOS, OpenGL 4.1 Docs: Quarto Licence: Apache-2.0 Made by Sisyphe

Getting started · Tutorials · Gallery · Coming from openFrameworks or p5.js · Examples

Shape ideas in code. Fire them into art.

Kiln's showcase: one window, twenty panels and a sound row, every API name
One live window exercising every name in the API: uv run python examples/showcase.py

Kiln is a fast creative-coding framework for Python. You subclass kiln.App, fill in setup, update and draw, and call functions like kiln.fill, kiln.circle and kiln.rect. Live state reads like data (kiln.mouse.x, kiln.clock.time), scopes are with blocks (with kiln.transform():), and modes are enums that also take strings (kiln.blend("add")). The naming page has the rules, and the changelog maps every 0.1 name to its 0.2 name. It runs on macOS on Apple Silicon. It draws in 2D and puts the GPU in reach: pictures, offscreen buffers, your own shaders, meshes and instanced geometry, TrueType text and GUI panels. 3D comes later.

Why it's fast. A drawing call never talks to the GPU. It does a little arithmetic in plain Python (it applies the current transform) and appends one record to a batch. At the end of the frame the whole batch is uploaded once and drawn in a handful of GPU calls, with each circle, rectangle, line or glyph evaluated as a signed distance field in the fragment shader, so there is no tessellation and edges are anti-aliased for free. For thousands of identical-style shapes, the bulk functions (circles, rects, lines, ellipses) take numpy arrays and build the records in one go. The GL layer is zengl with glfw.

Why Kiln

Sketch with agents. Sketch agents.

Sketch with agents. Kiln is the creative-coding framework your agents can see: a coding agent can write a Kiln sketch, run it, look at what it drew and fix it, without you in the loop. Three things make that work, and they help you as much as the agent:

  • A sketch renders to an array. kiln.testing.render_frames runs any App in a hidden window, with scripted mouse and keyboard input, and returns the last frame as a numpy array (every frame through on_frame). An agent, a test or a notebook on a Mac with a display session gets pixels back:

    from kiln.testing import MouseMove, render_frames
    
    frame = render_frames(Sketch(), frames=30, size=(800, 600), events=[(10, MouseMove(400, 300))])
    frame.shape  # (600, 800, 4): RGBA pixels to save, diff, or show to a model
    

    Every example takes --frames N to run N frames and exit. A shader that fails to compile raises an error that names the lines of your own GLSL.

  • Arrays in, arrays out. The bulk draw calls take numpy arrays and an image's pixels are an array you edit in place, so anything in Python that produces arrays, from PyTorch or MLX to a data frame, can feed a sketch from the same file.

  • One obvious way. One name per concept, a small public API (kiln, kiln.agents and kiln.testing), names a model already knows, and docs whose every code cell is executed, so the examples an agent learns from are known to run.

Sketch agents. kiln.agents puts agents inside the sketch: a Population kept as arrays, senses, steering forces and a behaviour tree that decides for every agent at once, so two thousand sparks flock and run from the mouse in examples/16_flocking.py. See the agents tutorial.

What's in it

  • Shapes and style. Circles, ellipses, rectangles, rounded rectangles, lines, triangles, and custom shapes with holes, curves and beziers (with kiln.shape()); a fill and a stroke in two colours (fill, stroke) and a tint for pictures; transforms and styles scoped by with, blend modes, bitmap and TrueType text; mouse, keyboard and file-drop events; simplex noise and colour (HSB, hex).
  • Images and textures. kiln.Image and kiln.Texture load PNG, JPEG and the rest, and draw them scaled, tinted, cropped (draw(source=)) and as thousands of sprites in one batch. Read and write pixels as numpy arrays, set filters, wrap modes and mipmaps, and copy the screen back (grab_screen, save_screen).
  • Fbos. with fbo: draws offscreen, in 8-bit or float, and the Fbo is a texture: posters and masks, feedback trails, and ping-pong simulations. Transparency composites as it does on screen.
  • Shaders. kiln.Shader colours anything Kiln draws with one GLSL function (vec4 shade(Material m)), keeping every shape's anti-aliasing; or loads a raw vertex and fragment program (Shader.load) for meshes and Vbos. Compile errors name your lines.
  • Meshes. kiln.Mesh holds vertices, colours, texture coordinates, normals and indices, draws every PrimitiveMode, loads .obj and .ply files, and warps a picture on a grid.
  • Vbos. kiln.Vbo keeps geometry on the GPU and draws it again without re-uploading, with partial updates and your own vertex attributes.
  • Instancing. Vbo.draw(instances=) and per-instance attributes draw thousands of copies of one shape in one call: a swarm of 4,096 fish at 0.5 ms a frame.
  • GPGPU. Float Fbos and raw shaders simulate on the GPU: a quarter of a million particles in a curl-noise flow.
  • Sound. Instruments you play from Python (kiln.Synth, kiln.Pluck, kiln.Sfx, drums such as kiln.Kick), effects, a step kiln.Sequencer, and a spectrum, waveform and level to draw from. It plays through the default audio device, from compiled code on its own thread, so a busy draw() never makes it stutter. Install with pip install "kiln-gl[sound]"; the sound tutorial makes a first beep.
  • Examples. A live showcase of the whole API, six original scenes (Abyss, after Haeckel, Pilgrim, inspired by thatgamecompany's Journey, and four agent scenes, Embers in a draught, Shoal, Meadow and Neuroevolution), a stress scene, nine ported sketches, seven sound examples, and nine remixes of well-loved sketches, read through the kiln (examples/, rendered in the gallery).

Install

Kiln needs macOS on Apple Silicon, Python 3.11 or newer and OpenGL 4.1. Install it from PyPI:

pip install kiln-gl
# or, with uv:
uv add kiln-gl

For sound, add the extra: pip install "kiln-gl[sound]" (or uv add "kiln-gl[sound]").

To work from source, clone the repository and use uv:

git clone https://github.com/slegroux/kiln && cd kiln && uv sync
# or add the development version to your own project:
uv add "kiln-gl @ git+https://github.com/slegroux/kiln"

Status

Kiln is 0.x: a minor version may break the API, and every break is listed in the changelog.

  • Stable for 0.x: the App lifecycle, shapes, Style and transforms, Color, Image, Texture, Fbo, Mesh, Font, the math helpers and run().
  • Experimental (may change in any release): kiln.agents, kiln.sound, the Shader material API (shade(Material), raw GLSL, custom Vbo attributes and instancing), kiln.testing, the more advanced Gui widgets and the presets JSON format.

A first sketch

import kiln


class Sketch(kiln.App):
    def setup(self):
        kiln.background(18, 16, 28)

    def draw(self):
        kiln.background(18, 16, 28)
        for i in range(12):
            kiln.fill(255, 140 + i * 8, 60, 200)
            kiln.circle(kiln.mouse.x + i * 20, kiln.mouse.y, 12 + i * 3)


kiln.run(Sketch(), 800, 600)

A window opens and a row of circles follows the mouse. ESC closes it.

Controls

kiln.Gui is a panel Kiln draws on top of your sketch, with sliders, toggles, buttons, labels, colour pickers, XY pads, ranges, knobs, choices, text fields, graphs, meters and folders. Make it in setup; each control returns a Parameter you read in draw like the value it holds:

class Sketch(kiln.App):
    def setup(self):
        self.gui = kiln.Gui("Circle")
        self.radius = self.gui.slider("radius", 70.0, 10.0, 200.0)
        self.ink = self.gui.color("ink", kiln.Color(255, 122, 69))
        self.shape = self.gui.choice("shape", ["disc", "ring"])
        self.pos = self.gui.xy("position", (0.0, 0.0))

    def draw(self):
        x, y = self.pos
        ring = self.shape == "ring"
        with kiln.style(fill=None if ring else self.ink, stroke=self.ink if ring else None):
            kiln.circle(320 + x * 100, 240 + y * 100, self.radius)

Click or drag a control, Shift for fine control, double-click to reset, Tab to hide the panel. Panels keep their clicks, never show up in save_screen, and gui.save(path) / gui.load(path) keep your settings as JSON. See the GUI tutorial.

Documentation

The docs site is not published yet. Build it locally (this needs the docs dependency group and Quarto 1.7 or newer):

uv sync --group docs
scripts/docs_publish.sh --render

then open http://localhost:4040. The script renders this checkout's docs and copies them to one fixed folder served at that address, so renders from any checkout or worktree show up at the same URL: refresh the tab. --render only re-renders the pages that changed since the last render (seconds, not minutes); the first run, or a change to a site-wide file such as _quarto.yml, renders everything. --full forces a full render, --page tutorials/gui.qmd renders just that page, and --render --dry-run lists what would re-render. Without a flag it publishes what was last rendered, and http://localhost:4040/published.txt says which branch and commit you're looking at. The site has:

  • Getting started: the sketch lifecycle and run().
  • Tutorials: shapes, colour, transforms, custom shapes, images, pixels and the screen, offscreen drawing, meshes and Vbos, shaders, GPGPU particles, instancing, blending and trails, noise and randomness, interaction, bitmap and TrueType text, GUI panels, agents and behaviour trees, ecosystems and evolved brains, performance, and sound (instruments, effects, a sequencer, drums, audio-reactive visuals and a whole track). Every code cell is executed and its render shown; animations are short looping videos.
  • Gallery: the showcase, the Abyss, Pilgrim, Embers in a draught, Shoal, Meadow and Neuroevolution scenes and the nine ported sketches, rendered, and the seven sound examples, each linked to its tutorial. The stress scene is in the Performance tutorial.
  • Remixes (in the Gallery menu): nine well-loved sketches read through the kiln, a picture of each.
  • Coming from: openFrameworks and p5.js, a page each: every name and its Kiln name, and every place Kiln behaves differently. Checkers (scripts/check_api_map.py, scripts/check_p5_map.py) keep both in step with the code.
  • Reference: the API, generated from the docstrings.

Public API. Everything you need is in kiln (kiln.circle, kiln.App, ...), in kiln.agents for agents and behaviour trees, and in kiln.testing for offscreen renders in tests. Those three namespaces are the public API. Modules such as kiln._window, kiln.graphics.renderer or kiln.graphics.tessellate are internals and may change without notice.

Examples

Twenty-four examples live in examples/, with nine remixes (remix_*.py) and logo.py, which draws the logo. First, Kiln's own:

uv run python examples/showcase.py          # every API name in one window: twenty panels and a sound row
uv run python examples/abyss.py             # Kunstformen der Tiefe: medusae and plankton, after Haeckel
uv run python examples/pilgrim.py           # a traveller, dunes and a scarf, inspired by Journey
uv run python examples/16_flocking.py       # two thousand sparks flock and run from the mouse, with a behaviour tree
uv run python examples/17_predators.py      # a shoal and its hunters: one population, one tree
uv run python examples/23_ecosystem.py      # a meadow: grazers eat, breed and die, and their genes drift
uv run python examples/24_neuroevolution.py # foragers whose small neural brains evolve to find food
uv run python examples/stress.py            # five layers that drive Kiln as hard as it goes

Then nine ported sketches (their sources are credited in NOTICE):

uv run python examples/01_graphics.py       # shapes, transparency, lines
uv run python examples/02_polygon.py        # winding rules, curves, draggable points
uv run python examples/03_blending.py       # blend modes
uv run python examples/04_noise_field.py    # 4,096 particles in a noise field
uv run python examples/05_image_loader.py   # a Ming jar, a halftone of 3,600 circles, a loupe
uv run python examples/06_fbo_trails.py     # 8-bit against float Fbo trails
uv run python examples/07_shader.py         # a vertex shader on a Vbo, a print-line fragment shader
uv run python examples/08_vbo.py            # 25 lit reliefs: Mesh against Vbo
uv run python examples/09_gui.py            # a panel of controls shapes a circle

And seven that make sound. They need the sound extra (uv sync --extra sound, or pip install "kiln-gl[sound]") and play through your speakers:

uv run python examples/10_synth.py          # a mouse synth: hold to play, pitch across, filter cutoff up
uv run python examples/11_audio_reactive.py # a synth plays, and its spectrum, waveform and level draw the picture
uv run python examples/13_sfx.py            # bouncing balls pluck a melody, and everything they touch goes bling
uv run python examples/14_drums.py          # a drum machine you can see: a 16-step beat at 122 BPM
uv run python examples/18_sequencer.py      # a step sequencer: the picture pulses exactly with the beat
uv run python examples/19_melodic_techno.py # a one-minute melodic techno track, with visuals on its beat
uv run python examples/20_gui_synth.py      # a synth you play and shape from a panel, with the sound drawn live

Add --frames 120 to any of them to run 120 frames and exit.

Performance

bench/bench_2d.py times the frame in a visible 800x600 window with vsync off (no pixel readback). Run it with uv run python bench/bench_2d.py --check. The numbers below are median frame times on the Apple M3 Pro development machine, from one run on 2026-10-10, after 20 warm-up frames and over 120 timed frames, at load average 3.7 on 12 cores (other jobs were running, so the bench flagged them as inflated; a quiet machine should do better).

Scenario Frame, median
10,000 circle calls 6.8 ms
10,000 rect calls 7.3 ms
1,000 five-vertex shape polygons 12.7 ms
2,000 bitmap glyphs 1.8 ms
10,000 circles (bulk) 0.41 ms
100,000 circles (bulk) 1.8 ms
200,000 circles (bulk) 3.9 ms
500 alternating SDF / custom-shape segments 3.6 ms
10,000 sprites of one texture 8.2 ms
1,000 sprites alternating two textures 3.3 ms
One Fbo pass (10,000 circles in, one draw out) 0.45 ms
A full-screen material (a 3-octave loop) 0.35 ms
A static 1M-vertex Vbo 1.4 ms
4,096 instances of 12 floats, rewritten every frame 0.53 ms

The project goal is 10,000 immediate-mode shapes within 16.7 ms (60 fps), 200,000 bulk circles, 10,000 sprites of one texture and 4,096 instances rewritten every frame within the same budget; all pass (--check exits 0). Immediate calls cost about 0.65 µs each in Python; bulk calls about 10-15 ns per shape.

Tests

uv sync
uv run pytest -q

The long seeded simulation runs of kiln.agents (the Meadow's population bands and its hunters' selection, and the river selection test, 10k-20k steps each) are marked sim and skipped unless asked for, also under CI's -m "not gpu". Run them on demand:

uv run pytest -m sim

uv sync installs the dev group with the package: pytest, ruff, and the two packages the docs-helper tests use (ipython and imageio-ffmpeg). Building the docs site needs the docs group as well (see Documentation).

Tests open hidden (and a few visible) windows. They run as a background app, so they never take focus from your terminal or editor. To run examples or the benchmark the same way, set KILN_BACKGROUND=1:

KILN_BACKGROUND=1 uv run python examples/01_graphics.py --frames 120
KILN_BACKGROUND=1 uv run python bench/bench_2d.py --check

The pixel tests open a hidden OpenGL window, so they need a macOS GUI session.

Licence

Kiln is licensed under the Apache License 2.0; see NOTICE for third-party material: the ported examples stay MIT (see examples/NOTICE), the bundled Inter font is under the SIL Open Font License 1.1, the bitmap font is public domain, and the sample image is dedicated to the public domain (CC0).

The Kiln name and logo, and the Sisyphe name and logo, are trademarks of Sisyphe LLC and are not covered by the code licence.

Built by Sisyphe

Kiln is built by Sisyphe.

Metadata

Release files for kiln-gl 0.2.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 kiln-gl 0.2.0
File Size Uploaded
kiln_gl-0.2.0.tar.gz 1.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for kiln-gl 0.2.0
File Interpreter ABI Platform
kiln_gl-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.1 MB

Release files / kiln_gl-0.2.0.tar.gz

Download URL kiln_gl-0.2.0.tar.gz
Size 1.6 MB
Tags Source
SHA-256 checksum
How to use checksums
abc75b197c38a8922380895024d61c646dbcb42787446d3ad31a95ee74a571d7
BLAKE2b-256 checksum
How to use checksums
a07903eea57808d5ebf1c72b3d23426ac7ff791919736143fb3619454be446b8
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 Oct 14, 2026.

Transparency log

Release files / kiln_gl-0.2.0-py3-none-any.whl

Download URL kiln_gl-0.2.0-py3-none-any.whl
Size 496.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bf9a410eae8e7141c10ed2c19a536ce6af84d3a67f20dba0f7a6dea40a1c588d
BLAKE2b-256 checksum
How to use checksums
00eeb1e47a23eef2a0676b3b3ae14336508ceec20aa23e980f04e0503f975809
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 Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

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