Archive

For UI developers / compiled runtime examples

Particle Physics and Canvas Runtime

This example separates a small physics model from browser animation. Bosatsu computes particle positions and canvas commands; the JavaScript runtime schedules frames and draws those commands. Run it, select Scatter, then Reset.

Code & run Read the complete source and run its generated UI

Particle state

Five particles share one state value. Each particle records position and velocity.

struct Particle(x: Float64, y: Float64, vx: Float64, vy: Float64)
struct AxisBounce(pos: Float64, vel: Float64)
struct System5(p1: Particle, p2: Particle, p3: Particle, p4: Particle, p5: Particle)

One physics step

The step adds gravity, advances position, and applies the bounce helper. This is a discrete simulation with a fixed time step, not a physical accuracy benchmark.

def step_particle(p: Particle, dt: Float64) -> Particle:
  Particle(px, py, pvx, pvy) = p
  gravity_acc = d_500
  vy1 = pvy +. (gravity_acc *. dt)
  nx = px +. (pvx *. dt)
  ny = py +. (vy1 *. dt)
  AxisBounce(fx, fvx) = bounce_axis(nx, pvx, min_pos, max_x)
  AxisBounce(fy, fvy) = bounce_axis(ny, vy1, min_pos, max_y)
  Particle(fx, fy, fvx *. damping, fvy *. damping)

Drawing from state

The renderer reads the system and constructs canvas commands. Constructing those commands does not draw them; the runtime interprets them.

def render_particles(ps: State[System5]) -> IO[CanvasCommands]: (
  sys <- ps.read().flat_map()
  match sys:
    case System5(a, b, c, d, e):
      c0: List[CanvasCommands] = []
      c1 = draw_particle(c0, "#ff6b8a", a)
      c2 = draw_particle(c1, "#4ecdc4", b)
      c3 = draw_particle(c2, "#ffe66d", c)
      c4 = draw_particle(c3, "#a8e6cf", d)
      c5 = draw_particle(c4, "#7c83fd", e)
      pure(sequence([clear("#1a1a2e"), *c5]))
)

Frame execution

Each registered frame advances the simulation by 0.016 modeled seconds. The browser controls when callbacks arrive, so simulated time need not equal elapsed wall time. The write is an IO effect executed by the runtime.

frame_update: IO[Unit] = (
  dt = d_16 /. d_1000
  ps <- particles.flat_map()
  current <- ps.read().flat_map()
  ps.write(step_system(current, dt))
)

frame_registration = on_frame(frame_update)

The excerpts above come directly from the deployed program. The drawer contains the complete file. UI edits currently need a local build.

How state writes update the DOM → · Make a calculator with editable browser source →