Orb is a language for software that has to follow rules. You declare the states and the events; the compiler builds the app or tells you why it can't.
An Orb program describes how a piece of software behaves: the data it holds, the states it can be in, the events that move it between them and what it shows at each step. You write it in .lolo, a small Lisp-flavoured notation. The compiler checks it, then either runs it directly or generates a full application from it.
Here is a complete program. It runs on this page:
Counting
on Counter · states: 1 · transitions: 4The program above as state machines, one per trait: each box is a state, each arrow an event that moves it, and the icons on an arrow are the effects that event runs. A ◇ on an arrow is a guard; hover it to read the check. How to read AVL
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:
(set @entity.count (+ @entity.count 1))Every Orb program is made of orbitals. An orbital is one self-contained piece of behavior:
Counter, with a count field);Counting);/counter).Orbital = Entity + Traits + Pages
Application = orbitals that talk to each other through eventsOrbitals never reach into each other. One trait emits an event, another listens for it, and the compiler checks that every event that is emitted has somewhere to go.
Each interaction follows the same loop:
Event → Guard → Transition → Effects → UI → EventPressing + sends INCREMENT. The trait is in state ready, which has a transition for INCREMENT, so it fires: its effect (set @entity.count (+ @entity.count 1)) updates the entity, and the screen, which is bound to @entity.count, shows the new value. The new screen offers the same buttons again, so the loop closes.
The compiler enforces the loop. A button whose event no state handles, a state you can never leave, an event emitted to nobody, or a page that renders nothing are all errors at orb validate, before anything runs.
Most business software is rules: an order cannot ship before it is paid, a refund needs a manager over a certain amount, a ticket must be answered within a day. In a typical codebase those rules are spread across the frontend, the API, the database and the tests, and they drift apart.
In Orb the rule is the shape of the program. If there is no SHIP transition out of pending, an unpaid order cannot ship, and that is checked, not hoped for. The same file is the specification, the implementation and the input to verification:
orb validate proves the program is well-formed (0 errors and 0 warnings is the bar);orb verify walks every transition and checks that each one does what it declares;orb compile generates a TypeScript app, and other backends, from the same source.You rarely write every state machine yourself. The standard library ships hundreds of behaviors (lists, forms, wizards, dashboards, games, learning instruments) that you import with uses and bind to your own data. You keep the parts that are specific to your domain; the library owns the rest. Composition explains how.
The language where the rule is the program.