Documentation

Introduction to Orb

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:

State machine

Counting

on Counter · states: 1 · transitions: 4
emitsMOVED
INIT
INCREMENT
DECREMENT
RESET
nowready
Running live

The 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

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

The unit: an orbital

Every Orb program is made of orbitals. An orbital is one self-contained piece of behavior:

  • an entity: the data it owns (Counter, with a count field);
  • one or more traits: state machines that react to events and change the entity (Counting);
  • zero or more pages: the routes where its traits appear (/counter).
text
1 2 Orbital = Entity + Traits + Pages Application = orbitals that talk to each other through events

Orbitals 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.

The closed circuit

Each interaction follows the same loop:

text
1 Event → Guard → Transition → Effects → UI → Event

Pressing + 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.

Why a language

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.

What you write

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.

Orb

The language where the rule is the program.

© 2026 Almadar. Ljubljana, Slovenia.
Not reported here