Documentation

Quickstart

Write a program, validate it, verify it and run it, in five minutes.

This walkthrough builds a counter: one orbital with one trait. It is small, but it is a complete Orb program, and every step is the same for a large one.

1. Write the program

Create counter.lolo:

lolo
1app counter "1.0.0" 2"A counter with increment, decrement and reset." 3orbital CounterOrbital { 4 entity Counter [runtime] { 5 id : string! 6 count : number = 0 7 } 8 9 trait Counting -> Counter [interaction, instance] { 10 initial: ready 11 12 state ready { 13 INIT -> ready 14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 15 { type: button, label: "−", action: DECREMENT, variant: secondary, aria-label: "Decrease" }, 16 { type: typography, content: @entity.count, variant: h2 }, 17 { type: button, label: "+", action: INCREMENT, variant: primary, aria-label: "Increase" }, 18 { type: button, label: "Reset", action: RESET, variant: ghost } 19 ] }) 20 INCREMENT -> ready 21 (set @entity.count (+ @entity.count 1)) 22 DECREMENT -> ready 23 (set @entity.count (- @entity.count 1)) 24 RESET -> ready 25 (set @entity.count 0) 26 } 27 } 28 29 page "/counter" -> Counting 30}

Read it top to bottom: the entity Counter holds one number; the trait Counting has one state, ready. On INIT (when the page opens) it renders a row of buttons and the count. Each button sends an event, and each event has a transition that updates the count. The text is bound to @entity.count, so it redraws when the count changes.

2. Validate

bash
1 orb validate counter.lolo

The bar is 0 errors and 0 warnings. Try breaking it: delete the RESET -> ready transition and validate again. The compiler reports that the Reset button sends an event no state handles. That is the closed circuit at work: every affordance must lead somewhere.

3. Verify

bash
1 orb verify counter.lolo

Verification plays every transition and checks that it does what it declares: the state it lands in, the effects it runs, the screen it renders.

4. Run it

bash
1 orb serve counter.lolo --open

orb serve compiles the program to a TypeScript app and serves it with a bundled runtime, with no Node.js needed. Here is the same program, running on this page:

State machine
Counting.lolololo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 trait Counting -> Counter [interaction, instance] { state ready { INIT -> ready (render-ui main <Stack align={center} direction={horizontal} gap={md}> <Button action={DECREMENT} aria-label={Decrease} label={"−"} variant={secondary} /> <Typography content={@entity.count} variant={h2} /> <Button action={INCREMENT} aria-label={Increase} label={"+"} variant={primary} /> <Button action={RESET} label={Reset} variant={ghost} /> </Stack>) INCREMENT -> ready (emit MOVED { event: INCREMENT }) (set @entity.count (+ @entity.count 1)) DECREMENT -> ready (emit MOVED { event: DECREMENT }) (set @entity.count (- @entity.count 1)) RESET -> ready (emit MOVED { event: RESET }) (set @entity.count 0) } emits { MOVED -> external { event : string! } @description "Each move names the event it took, so the diagram beside the demo can follow it." } }
INIT
INCREMENT
DECREMENT
RESET
nowready
Running live

std-orb-docex-counter.lolololo
1app std-orb-docex-counter "1.0.0" 2"std-orb-docex-counter — the smallest complete orbital: one entity, one trait with one state, three buttons and a closed circuit." 3orbital CounterOrbital { 4 entity Counter [runtime] { 5 id : string! 6 count : number = 0 7 } 8 9 trait Counting -> Counter [interaction, instance] { 10 initial: ready 11 12 state ready { 13 INIT -> ready 14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 15 { type: button, label: "−", action: DECREMENT, variant: secondary, aria-label: "Decrease" }, 16 { type: typography, content: @entity.count, variant: h2 }, 17 { type: button, label: "+", action: INCREMENT, variant: primary, aria-label: "Increase" }, 18 { type: button, label: "Reset", action: RESET, variant: ghost } 19 ] }) 20 INCREMENT -> ready 21 (emit MOVED { event: "INCREMENT" }) 22 (set @entity.count (+ @entity.count 1)) 23 DECREMENT -> ready 24 (emit MOVED { event: "DECREMENT" }) 25 (set @entity.count (- @entity.count 1)) 26 RESET -> ready 27 (emit MOVED { event: "RESET" }) 28 (set @entity.count 0) 29 } 30 31 emits { 32 MOVED -> external { 33 event : string! 34 } 35 @description "Each move names the event it took, so the diagram beside the demo can follow it." 36 } 37 } 38 39 page "/counter" -> Counting 40}

What happens when INCREMENT fires:

readyINCREMENT
→
ready
Effects (1)What runs, in order, when the transition fires.
(set @entity.count (+ @entity.count 1))
@entity.count@entity.count1+set

5. Compile it

bash
1 orb compile counter.lolo --output ./counter-app

This writes a full application you can deploy anywhere. The program is the source; the generated code is never edited by hand.

Orb

A programming language of composable state machines.

© 2026 Almadar. Ljubljana, Slovenia.
Not reported here