← All work

Generative art · Independent project

MathRender

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
Clifford attractor. A two-dimensional iterative map, rendered as a field of accumulated points.

Art for a particular room

I wanted artwork for my office, so I built a studio for making it from mathematics. The room gave me a concrete brief: work that would sit comfortably against walnut slats, with a palette, scale, and arrangement that belonged there.

The local application, FORM, lets me explore an equation, develop related variations, compose a collection against a photograph of the room, and prepare the files and measurements for printing and hanging. A saved recipe connects those steps.

The equation makes the form

The studio includes 30 mathematical systems across chaos, curves, knots, fields, and fractals. Each needs an appropriate numerical representation: 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. Standard quality uses RK45; the higher settings use DOP853 with tighter tolerances. I keep the solver samples independent of print resolution, so changing the paper size does not silently change the trajectory.

The Lorenz system
dx/dt = σ(y − x)
dy/dt = x(ρ − z) − y
dz/dt = xy − βz
The solver produces a three-dimensional path. Projection, color, and line treatment determine how that path appears on the page.
De Jong: successive points from an iterative map accumulate into a density image.
Hopf fibers: circles sampled on the three-sphere and projected into three dimensions.
Paths and points
Lorenz and other ODEs produce trajectories. Clifford and De Jong use simultaneous updates from the previous state; their images show where the resulting points accumulate.
Curves and fields
Parametric knots and spirals become paths. Chladni patterns and interference fields produce contours or raster images. Newton basins use iterative root finding to color regions of the plane.
Numerical boundaries
The engine checks nonfinite values and divergent solutions. Invalid spans are split into separate paths, keeping a discontinuity from becoming an accidental line across the artwork.

Explore a family of possibilities

Adjusting one slider at a time was too narrow a way to search for a piece. I built an explorer that renders twelve related studies, lets me favorite the promising ones, and uses those selections as parents for another round. Gentle, Balanced, and Bold control the amount of change.

Each child is an independent recipe with a deterministic seed. The generator cycles through the chosen parents and applies bounded changes. For the built-in chaotic ODEs, it preserves the equation coefficients and integration budgets while varying initial conditions and presentation. Colors, typography, and print settings stay together as a series.

The interface renders two previews at a time, keeps the history of each round, and shows failures on the affected card. Closing the explorer cancels unfinished work; choosing a favorite only changes the main composition when I select “Use in studio.”

Twelve rendered studies per round. Selection drives the next set of parameter variations.

Compose the print, then inspect it

The studio separates the mathematical form from its presentation. I can change the projection, rotation, crop, line weight, inks, border, and lettering while keeping the underlying recipe. Paper dimensions and margins are part of the composition from the start.

Inspection zoom changes only the view on screen. Artwork scale changes the printed composition. Keeping those controls separate matters when a pleasing thumbnail still needs to work at its intended size.

For three-dimensional forms, a projection set creates XY, XZ, and YZ studies from the same equation, parameters, and seed. It is a way to develop a coordinated collection by changing the viewpoint.

The main studio keeps the artwork beside its mathematical and physical print settings.
Related views of one form can become separate prints in the same collection.

Design for the wall it will live on

I brought a photograph of the office into the planner so I could judge the work alongside the wood, paint, furniture, and lighting. Collections can share inks and typography while mixing portrait, landscape, and square formats.

The planner works in inches or centimeters. Prints have real paper and frame dimensions, can snap to a grid or align to one another, and report overlaps and gaps. Each item is a recipe snapshot, so editing the original study does not unexpectedly change a print already in the collection.

The room photograph is calibrated to the 48-inch slat-panel width. Its vertical placement is approximate; the hanging plan uses the explicit wall and frame measurements. That separates visual judgment in the photograph from the dimensions needed to hang the work.

An app preview over my office photograph, used to compare sizes and arrangements. The framed artworks are composited into the scene.
An exported hanging-plan example for a separate three-print collection. Plans record frame dimensions and positions and are available as PDF, SVG, and CSV.

A local rendering system that stays usable

React and TypeScript handle the studio, variation explorer, and wall planner, with Vite for development and bundling. A local FastAPI service validates recipes and runs the numerical and rendering work in Python. NumPy and SciPy generate the forms; Matplotlib and Pillow turn them into previews, proofs, and exports.

The difficult part is keeping iteration responsive while supporting large files. A geometry cache is keyed by the mathematical definition, parameters, custom expressions, seed, and cache version. Camera, color, typography, and PPI are separate, so a finish change can reuse the mathematical work.

Preview and final
Previews cap their long edge at 1,400 pixels. Final exports reuse stored paths, but rebuild density bins and evaluate scalar or Newton grids at output resolution. A print is rendered from its recipe, not enlarged from the preview.
Cancelable workers
At most two child processes run concurrently, with only one full export at a time to leave capacity for preview work. Cancellation terminates active work, and time limits stop long-running requests. Estimated-memory checks screen large raster exports before they run. Collection files render one at a time.
An expression language
The custom-function editor parses an allowlisted arithmetic syntax tree. It supports named variables and mathematical functions, with limits on depth, operations, and exponents. Attribute access, indexing, imports, and arbitrary Python execution are excluded.
Recoverable studies
Browser storage keeps studies, variation history, and wall arrangements. JSON recipes make them portable. An active print-collection export can be recovered after a browser reload while the server remains running; render jobs do not survive a server restart.
  1. Recipe

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

  2. Geometry

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

  3. Output

    Render a preview, an actual-scale crop, or the final file using the requested physical dimensions.

Chaotic trajectories depend on solver settings and numerical-library versions. Recipes and locked dependencies preserve the configuration; identical pixels across different library versions are not promised.

More workStillRoom

Project image