Drawing with RocRay
This example uses the RocRay platform to draw an interactive circle. Moving the mouse moves the circle, and holding the left mouse button changes its color, showing how a small graphical app keeps state, responds to input, and draws each frame.
import rr.App import rr.Color import rr.Draw Model : { position : { x : F32, y : F32 }, pressed : Bool } Msg : [] program = { init!, update!, render! } init! : App.Init(Model, []) init! = App.init( App.default.with_title("Follow the mouse").with_size({ width: 640, height: 480 }), |_io| Ok({ position: { x: 320, y: 240 }, pressed: False }), ) update! : Model, App.Input(Msg), App.Io => Try(Model, [Exit(I64)]) update! = |_model, input, _io| Ok({ position: input.devices.mouse.position(), pressed: input.devices.mouse.button_down(Left), }) render! : Model, Draw.Frame => Try({}, [Exit(I64)]) render! = |model, frame| { frame.clear!(Color.black) frame.circle!({ center: model.position, radius: 30, style: Draw.filled( if model.pressed { Color.red } else { Color.blue }, ), }) Ok({}) }
The three callbacks divide up the work: init! creates the initial model, update! records the mouse position and button state, and render! draws the circle from that model. The platform handles the window and closes it when you press Escape.
RocRay releases are paired with particular Roc versions. This app header selects a specific RocRay version and its supported nightly together:
app [Model, program] { roc: "nightly-2026-10-28-bad769b", rr: platform "https://github.com/lukewilliamboswell/roc-ray/releases/download/0.11.1/4NWnSt7bKCyVx4xDFH89pV11ekYbiGQ43kdjfZ9NBJQn.tar.zst", }
Output

The circle is blue when the mouse button is released and turns red while the left button is held.
From this directory, build with roc build main.roc, then run ./main (main.exe on Windows). The release bundle already includes the compiled host, so you do not need Zig to build the app.
The default bundle supports Windows x64, macOS (both Intel and Apple Silicon), and Linux x64 (using X11 or XWayland).
For an unattended smoke test, you can run ./main --host-headless --host-headless-frames=3. This runs a few cycles without opening a window, which is useful for checking that the app executes successfully. Checking its appearance still requires running it with rendering enabled.
Validated with Roc nightly-2026-10-28-bad769b.