Skip to content

Architecture ​

Packages ​

PackageWhat it doesDepends on
@isora/coreScene model and validation, projection and depth sorting, placement and stacking, sitting, pathfinding, behavior planning, motion, computing parts to drawNothing (pure TS, no DOM)
@isora/react<IsoScene>, drawing layers, playback hooks (clock, behavior, timelines, scene changes)core, React 18/19
@isora/assets10 official asset packs (common, office, factory, city, medical, military, school, retail, transit, people; CC0)core
@isora/packPack manifest, validation and build, SVG sanitizing, static SVG exportcore
@isora/cliisora init, studio (local server), pack, exportcore, pack
apps/studioGUI editor (MUI)react, assets
example/*Examples that use the framework like a real app (claude-office, nullus-login, showcase: ten large scenes built in code)react, assets

Core modules and flow ​

 types · asset(defineAsset) · scene(validation·parse)
          │
 projection(iso·rotation·projectPoint) ── geometry(convex hull·SAT) ── shape(part boxes)
          │                                                                  │
 placement(stacking·floor height·floorPointAt) ── seating(spots·SITTING·sit path check·describeRig)
          │                                                                  │
 navigation(grid·A*·time reservations)                                       │
          │                                                                  │
 activities(activities·routines) ── behavior(planning·commands·frameAt) ── motion(gait·pose blending·transitionClear)
                                        │                                    │
                                  depth(front/back graph) ── render(prepareRender·composeRender·buildRenderList)
 timeline(paths·keyframes) ──────────────────────────────────────────────────┘
 builder(build scenes in code) · lint(layout checks) · motionLint(motion physics checks) ─ tools that only read the layers above
  • Dependencies only go data → plan → pose → picture. Lower layers do not know about upper ones.
  • The plan (behavior) is a function of time. Random numbers come from person id and seed, and the plan only grows in order of decision time.
  • The pose (motion) turns the values the plan gives for one moment (position, pose, transition progress, distance walked) into part boxes. It has no state.
  • The picture (render) turns boxes into screen faces and decides front and back. It splits static and moving things to keep per-frame cost low.
  • Text shown to people (lint messages, describeRig warnings, asset, state, activity and routine names) is English by default with Korean built in (Languages).

react ​

LayerWhat
Clock createTimelineClockTick (80 ms) notifications + per-frame time timeAt. Behavior and timelines can share it
Playback hooksuseBehaviorPlayback (routines, commands) · useTimelinePlayback (cargo, keyframes) · useObjectMotion (scene data changes)
Per-frame sourceLiveSource { ids, sample(wall), until? } — the hooks provide these, and apps can build their own
LayersLiveSceneLayer (prepared + sources → every frame) · SceneLayer (parts → SVG) · GroundGrid · HullOutline · ActivityBubbles
Viewer<IsoScene> — all of the above, assembled. Apps hook in through behavior, clock, renderOverlay and onSceneClick

Ticks and frames ​

Tick (80 ms)Frame (rAF)
What changesActivities and phases (people), device and door states, time of day, timeline cargoParts of walking and sitting people
Whereclock.subscribe → hooks → React re-renderLiveSceneLayer's store → only that layer re-renders
Why splitFull re-renders and rebuilding the static picture are expensiveWalking chopped into ticks would jitter

Extension points ​

You want toUse
Move people on app eventsuseBehaviorPlayback().command / BehaviorWorld.command
Turn constantly changing data into people's movementuseActorCommands
Build scenes in codecreateSceneBuilder
Define routines and schedulesScene routines, clock
Add a new activity (e.g. charge)Write the name in furniture seats[].activity or spots[].activity and use it in a routine
Move non-people every frame (live forklift or AGV positions)Build a LiveSource and put it in LiveSceneLayer
Marks above heads, pins, path lines<IsoScene renderOverlay> or LiveSceneLayer children
Pick a location by clicking<IsoScene onSceneClick>, floorPointAt
A new person assetCustom people, describeRig
Another renderer (Canvas, WebGL, server images)Faces from prepareRender and composeRender, boxes from actorPartBoxes and objectPartBoxes
Bind equipment states<IsoScene states> — the asset's states names
Show names and messages in another languagenames / labels next to the English text, assetName · stateLabel · localize, { locale } on lint (Languages)

Checklist: is it usable as a framework? ​

AreaStatusNotes
Scene and asset data model✅Schema validation (parseScene, validateScene, defineAsset), pack id rules, deterministic JSON
Coordinates, projection, click coordinates✅projectPoint, unprojectPoint, floorPointAt, onSceneClick
Placement, stacking, collisions✅placeAt, surfaceHeightAt, findCollisions
Autonomous people behavior✅Routines, schedules, making way, coming and going; deterministic
Commanding people✅go, do, idle, leave, routine; fallback commands (orElse); standing activities; gathering around; interrupting, releasing spots, reporting results; replaying the log
Data → movement✅Declare what each person should do with useActorCommands (example/claude-office)
Building scenes in code✅createSceneBuilder (add, sit, floor)
Reading positions and activities✅people, overrides, frameAt(t), per-frame renderOverlay
Playback control✅Shared clock, seek, setSpeed, reduced motion
Motion quality✅Every frame in world space, sit-path collision checks
Extending person assets✅Part naming conventions + describeRig
Swapping the renderer✅The core provides faces and boxes without the DOM
Languages✅English by default, Korean built in; translations sit next to the English text
Command-finished events△Compare people every tick instead of a callback (Moving people)
Pathfinding commands for non-people△Forklifts and AGVs get positions from the app through a LiveSource. Only people pathfind and make way
Floors and stairs✗One floor only. Moving between floors is not supported yet
Continuous rotation✗Facing turns instantly in 90° steps
Renaming the rig✗Part naming rules (leg-l etc.) are fixed. To use other names, rename the parts to match
Long jumps△Seeking an hour ahead takes a few hundred ms of planning

What the tests guard ​

  • packages/core/src/behavior.test.ts — planning, making way, schedules, commands (interrupting, releasing spots, coming and going, failures), determinism, no overlaps when commands and routines mix
  • packages/assets/src/motion.test.ts — sitting in desk chairs and sofas facing four directions never passes through furniture for any person, transition continuity, gait, depth order, rig conventions, the doc example asset
  • apps/studio/src/editor/samples.test.ts — every desk, sofa and bench seat in the sample scenes passes the sit-path check
  • packages/react/src/IsoScene.test.tsx — app-owned behavior playback, overlays
  • example/claude-office/src/office.test.ts — sessions → desk assignment; per-mood commands (code, coffee, Zz) succeed without overlaps even when spots run short

Released under the MIT License.