RocRay is a platform for games, visualizations, and other frame-oriented Roc applications.
An app provides init!, update!, and render!. init! creates the first model with App.Io authority. Each host cycle, update! receives that same authority and folds one App.Input into the next model; render! may then draw that model through a Draw.Frame.
Select services with methods such as io.files() and io.http(). Reach beyond the app's own resources -- network origins, directories, programs, environment variables, the clipboard -- is declared in the startup config with Permission, and the declaration is the grant. Phase rules and resource bounds still apply.
Host-state effects are legal in init!, update!, and tasks. Drawing is legal only in render!. Waiting effects are legal in init!, where they block startup, and in tasks, where they park the task. Each effect documents its exact phases. A phase violation stops the app as a programmer error.
Start with App, then use Draw, Devices, Assets, Audio, and Task as needed. Complete examples are available in the repository.
This app opens a window and draws a circle. App.default ends it when Escape is pressed, so it needs no quit logic of its own:
app [Model, program] { rr: platform "../../platform/main.roc" }
import rr.App
import rr.Color
import rr.Draw
Model : { frames : U64 }
Msg : []
program = { init!, update!, render! }
init! : App.Init(Model, [])
init! = App.init(App.default.with_title("Hello"), |_io| Ok({ frames: 0 }))
update! : Model, App.Input(Msg), App.Io => Try(Model, [Exit(I64)])
update! = |model, _input, _io| Ok({ frames: model.frames + 1 })
render! : Model, Draw.Frame => Try({}, [Exit(I64)])
render! = |_model, frame| {
frame.clear!(Color.black)
frame.circle!({ center: { x: 400, y: 300 }, radius: 40, style: Draw.filled(Color.red) })
Ok({})
}