A functional toolkit for 3D games.
Write your game in Functor Lang — a tiny interpreted language — and edit it live while it runs, with the model preserved mid-frame. Every session records itself, so you can pause the running game and scrub back through its timeline.
This is live. Change a number and watch it take hold mid-wave — or drag the timeline to rewind the scene.
Model–View–Update, all the way down. This is real Functor Lang — hit ▶ try it to open it live in the sandbox.
// a pulsing sphere with a beat counter
type Msg = | Beat
let init = { spin: 0.0, beat: 0.0 }
let tick = (model, dt: Float, tts: Float) =>
{ model with spin: model.spin + dt }
let update = (model, msg) =>
match msg with
| Beat => { model with beat: model.beat + 1.0 }
let subscriptions = (model) => Sub.every(Time.seconds(1.0), Beat)
let draw = (model, tts: Float) =>
Frame.create(
Camera.lookAt(0.0, 2.0, -6.0, 0.0, 0.0, 0.0),
Scene.sphere()
|> Scene.emissive(1.0, 0.3, 0.8)
|> Scene.scale(1.0 + 0.2 * Math.sin(model.spin * 4.0)))▶ try it
Every session records itself as it runs. Pause, drag the timeline back, and single-step forward — the whole game replays, deterministically.
Save an edit and the runtime swaps the program under the running model. Closures rebind to the new code and their captured values carry over — the ball keeps bouncing, mid-arc, with your new gravity.
A game is pure Model–View–Update. Effects are values too — randomness, time, network —
performed by the shell and folded back through update.
The same .fun runs on desktop GL, browser WebGL2, and Quest OpenXR — one
renderer behind all three: primitives, glTF, lighting and shadows, physics, spatial
audio.
model → Frame function.
Nothing to install — write Functor Lang in the browser and watch it hot-reload live.
▶ Open the sandbox