← All work

Generative art · Independent project

FORM

From a mathematical idea to art for my office.

My role
Concept, interface, numerical rendering, and print workflow
When
2026
Built with
React / TypeScript, FastAPI, NumPy, SciPy, Matplotlib, Pillow, Codex
Fourier, Interference, and Superformula: three FORM prints on my office wall.

Art for a particular room

I wanted art for my office, so I built a studio for making it from mathematics. The room set the brief: work that would sit well against walnut slats, in a palette, size, and arrangement that belonged there.

FORM lets me explore an equation, breed variations, lay out a collection over a photo of the room, and export the files and measurements to print and hang it. A saved recipe carries each piece through every step. Three of its prints now hang in that office.

Equations

FORM includes 30 mathematical systems across chaos, curves, knots, fields, and fractals. Each needs its own numerical treatment: a sampled trajectory, a set of paths, a point-density image, or a field evaluated on a grid.

For the Lorenz piece, SciPy integrates three coupled differential equations, with RK45 at standard quality and DOP853 with tighter tolerances above it. The number of solver samples doesn’t depend on the paper, so printing larger never changes the shape.

The Lorenz system
dx/dt = σ(y − x)
dy/dt = x(ρ − z) − y
dz/dt = xy − βz
The solver produces a path in three dimensions; projection, color, and line weight decide how it lands on the page.
De Jong: points from an iterative map pile up into a density image.
Hopf fibers: circles on the three-sphere, projected into three dimensions.
Paths and points
Lorenz and the other differential equations trace paths. Clifford and De Jong are maps that update every coordinate at once; their images show where the points pile up.
Curves and fields
Knots and spirals become paths. Chladni patterns and interference fields become contours or raster images, and Newton basins color the plane by which root each point converges to.
Breaks in the math
When a solution diverges or produces invalid numbers, the path is split there, so a gap in the math never becomes a stray line across the print.

The variation explorer

Moving one slider at a time was too slow a way to find a piece. The explorer renders twelve related studies, I favorite the promising ones, and those become the parents of the next round. Gentle, Balanced, and Bold set how far each round can move.

Every study is its own recipe with a fixed seed, so any of them can be reproduced. For the chaotic systems, the explorer keeps the equation’s coefficients and varies the starting point and presentation, so a family stays recognizably related. Nothing in the main studio changes until I choose “Use in studio.”

Twelve studies per round. Favorites become the parents of the next one.

Composing a print

The mathematical form and its presentation are kept apart. Projection, rotation, crop, line weight, inks, border, and lettering can all change while the recipe underneath stays the same, and the paper size and margins are part of the composition from the start.

For three-dimensional forms, a projection set renders the XY, XZ, and YZ views of one equation and seed. That’s how one form becomes a matching set of prints.

The studio keeps the artwork beside its math and its print settings.
Three views of one form, ready to become three prints.

Planning the wall

I brought a photo of the office into the planner to judge prints against the wood, paint, furniture, and light. The preview below is one arrangement I tried, led by a Lorenz print; what I hung in the end is the photo at the top of this page. Prints have real paper and frame sizes, snap to a grid or to each other, and flag overlaps and gaps, in inches or centimeters.

Each print in a collection is a snapshot of its recipe, so reworking the original study later doesn’t change a print that’s already planned. The photo is scaled to the slat panel’s 48-inch width for judging size, and the hanging plan uses measured wall and frame dimensions.

Three prints laid out over a photo of my office in the wall planner.
A hanging plan for another collection, with frame sizes and positions, exported as PDF, SVG, or CSV.

Under the hood

The studio, explorer, and wall planner are React and TypeScript, built with Vite. A local FastAPI service validates recipes and does the math and rendering in Python: NumPy and SciPy for the forms, Matplotlib and Pillow for previews and exports. I built FORM with help from GPT-6.1 Sol in Codex.

The hard part was staying responsive while also producing 10,000-pixel files. Geometry is cached by equation, parameters, custom expressions, and seed, separately from camera, color, type, and resolution, so changing how a piece looks never redoes the math.

Preview and final
Previews stop at 1,400 pixels on the long edge. Final exports reuse the stored paths but rebuild density images and grids at full resolution, so a print is rendered, never enlarged.
Two workers
At most two render processes run at once, and only one can be a full export, which keeps previews quick. Long jobs can be cancelled, have time limits, and are checked for memory before they start.
Custom equations
The equation editor accepts arithmetic, named variables, and math functions, parsed from an allowlisted syntax tree with limits on depth and size. It can’t import modules, index into objects, or run arbitrary Python.
  1. Recipe

    Validate the equation, parameters, seed, composition, and print settings, and keep a versioned JSON copy.

  2. Geometry

    Solve or sample the form, cache it, and apply the projection and composition.

  3. Output

    Render a preview, a full-resolution proof, or the final file at its physical size.

More workStillRoom

Project image