Lighting Lab is a browser-based prototype for exploring how a programmable architectural lighting system could be organized around a stylized landmark tower. The architecture is intentionally split so rendering, runtime behavior, scene logic, and safety logic remain understandable and independently evolvable.
The tower in this project is fictionalized. It is not a digital twin of Tokyo Skytree or any other real-world landmark.
The presentation layer is built from React components and Tailwind-driven styling:
src/components/layout: top-level shell and page framingsrc/components/sidebar: scene library, details, system state, and control surfacessrc/components/tower: React Three Fiber viewport, tower mesh, and fixture renderingsrc/components/ui: shared presentation primitives such as badges
This layer is responsible for interaction, layout, typography, and visual polish. It does not own domain behavior.
The runtime coordinator lives primarily in src/hooks/useLightingSystem.ts.
It resolves:
- current operating mode
- scheduled scene selection
- manual scene holds
- override activation and expiration
- safety snapshot aggregation
- UI-facing runtime state
This layer is the bridge between pure domain logic and the rendered interface.
The scene engine remains data-driven and pure:
- src/data/scenes.ts: scene definitions
- src/lib/scene-engine/effects.ts: reusable effect evaluators
- src/lib/scene-engine/transitions.ts: transition blending
- src/lib/scene-engine/engine.ts: per-fixture lighting output
The engine computes lighting state over time without knowing anything about React components or UI layout.
Scheduling logic lives in src/lib/scheduler/index.ts, with schedule data in src/data/schedule.ts.
This layer resolves:
- the current scheduled scene
- the next scheduled scene
- minutes until the next handoff
- runtime mode precedence
- override activation and reversion
Safety and validation logic live in src/lib/constraints/index.ts.
This layer is responsible for:
- scene validation
- fallback scene selection
- brightness capping
- frame smoothing
- safety issue reporting
The goal is to keep safety behavior explicit, observable, and separate from scene authorship.
At a high level, the runtime works like this:
- The scheduler resolves the current scheduled scene.
- Mode logic decides whether scheduled, manual, or override playback should win.
- The constraint layer validates the requested scene and may substitute a fallback.
- The scene engine computes per-fixture emissive color and intensity.
- Constraint logic caps and smooths the frame.
FixtureLayerapplies the final values to the existing fixture materials.
This keeps the visual layer thin while the runtime stays testable and predictable.
Static runtime data:
scenes.tsschedule.tsoverridePresets.tssystemState.ts
Shared TypeScript contracts for scenes, tower fixtures, runtime state, schedules, overrides, and safety telemetry.
Runtime orchestration hooks. At the moment, useLightingSystem is the main coordinator for application state.
Pure domain logic:
scene-engineschedulerconstraintsoutputsreserved for future downstream adapters
- Keep rendering separate from logic.
- Keep scenes declarative and data-driven.
- Treat fixtures as individual addressable units.
- Make runtime state legible in the UI.
- Favor composable modules over component-level hardcoding.
- Preserve the stylized-tower framing so the project remains clearly fictionalized.
The current demo includes:
- a polished public-facing control deck
- a stylized tower with addressable fixtures
- animated, layered scenes
- scheduling and override behavior
- safety-aware output handling
The main future expansion areas are richer diagnostics, output adapters, and more advanced operational tooling.