The studio
fate-studio is an embeddable, self-hosted chart viewer and live simulator that renders and drives any fate machine in the browser. Pick a machine, watch the active configuration light up as you send events, inspect context, fire timers, and resolve invocations — all live, over Server-Sent Events.
The picture is never hand-drawn: it is generated from the machine's own structure, so the diagram can never drift from the code.
What you can do in it
- Browse a gallery of machines and open any one as an interactive diagram.
- Simulate live: send events by clicking a state's transitions, then undo, reset, import/export a snapshot, or replay a timeline.
- Inspect the active state path, context (updating live), and event history.
- Drive effects: a Pending effects panel fires a delayed (
after) timer or resolves/rejects an invocation with JSON output — exactly as a real adapter would, so the effect model is visible. - Read dense charts: an auto-layout canvas (ELK) with orthogonal wiring, a re-tidy control, and automatic badging of global/hub transitions so a
TERMINATE-from-everywhere doesn't bury the chart.
Why it's a separate project
The studio lives in its own repository (github.com/arisros/fate-studio) on purpose: the engine has no dependencies, and the studio needs a web server and a browser toolchain. Keeping them apart means adopting the engine never drags in net/http or a UI build. This split is the refactor behind v0.4.0 — see the changelog.
Embed it in your app
A studio is a Server you register machines on. Each entry supplies a Build function (the static descriptor, for the diagram) and an optional BuildLive function (a fresh actor, for the simulator):
import studio "github.com/arisros/fate-studio"
srv := studio.NewServer("my app — statecharts")
srv.Register(studio.Entry{
Name: "checkout",
Summary: "Cart → payment → fulfilment.",
Build: checkoutMachine.Describe, // structure for the chart
BuildLive: func() studio.LiveInstance { // a running actor for the sim
return studio.NewLiveActor(
checkoutMachine,
dispatch, // map an event name → typed event
checkoutMachine.Describe,
)
},
})
log.Fatal(srv.ListenAndServe(":8090"))dispatch is the one piece of glue: it turns an event name (what a button click sends) into your typed Evt. The rest — graph endpoint, SSE stream, sessions, effects — the studio provides. The whole UI is embedded in the binary, so this is a single self-contained server.
A real production case
The studio isn't just for demos. In the LORA loan-origination platform, the four human-task state machines that run in production — e-sign, underwriting, BPKB review, and survey — are registered in exactly the form above and served at fate-studio-dp.arisjirat.com. The same Machine values that drive real Temporal workflows are the ones you walk and simulate in the browser.
func registerSurvey(srv *studio.Server) {
describe := func() fate.MachineDescriptor { m, _ := svfsm.NewMachine(); return m.Describe() }
dispatch := func(name string) (svfsm.Evt, error) {
switch name {
case svfsm.EventSubmitPin: return svfsm.EvtSubmitPin{PIN: "1234"}, nil
case svfsm.EventForward: return svfsm.EvtForward{}, nil
// … one case per event the machine accepts …
case svfsm.EventTerminate: return svfsm.EvtTerminate{}, nil
}
return nil, studio.ErrUnknownEvent{Name: name}
}
srv.Register(studio.Entry{
Name: "survey",
Summary: "Survey — 4 parallel regions (main / head_vd / env_check / unscheduled).",
Build: describe,
BuildLive: func() studio.LiveInstance {
m, _ := svfsm.NewMachineWithContext(svfsm.Ctx{PIN: "1234", BPKBOnHand: true})
return studio.NewLiveActor(m, dispatch, describe)
},
})
}Survey is a genuinely large machine — 33 states across four parallel regions, 125 transitions — and it renders and simulates live. It's the worked example behind Keeping dense charts readable.
On the command line
If you just want to render or diff a machine without a browser, the engine ships a small CLI:
go install github.com/arisros/fate/cmd/fate@latest
fate mermaid machine.json
fate diff a.json b.jsonSee Install → The CLI.
Links
- Live demo studio: fate-studio.arisjirat.com
- Production case (LORA): fate-studio-dp.arisjirat.com
- Source: github.com/arisros/fate-studio
- Engine: github.com/arisros/fate
- How the chart is generated → a technical walk-through.