Generative art · Independent project
FORM
From a mathematical idea to art for my office.
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.
dx/dt = σ(y − x)
dy/dt = x(ρ − z) − y
dz/dt = xy − βz- 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.”
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.
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.
Printing at full size
Exports render the recipe again at the real size instead of enlarging a preview. A 36 × 24-inch Lorenz print at 300 PPI is 10,800 × 7,200 pixels, so before printing I check a 3-inch proof at full resolution, where the fine lines actually show.
PNG and TIFF exports carry an sRGB profile, and SVG and PDF keep line work as vectors. A collection export bundles the files, their recipes, a size-and-quantity list, and the hanging plans. Color management for a particular printer, including CMYK, happens outside FORM.
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.
Recipe
Validate the equation, parameters, seed, composition, and print settings, and keep a versioned JSON copy.
Geometry
Solve or sample the form, cache it, and apply the projection and composition.
Output
Render a preview, a full-resolution proof, or the final file at its physical size.