Skip to main content
Version: Next

Boot sequence

The app boots from src/main.ts. The module order matters: the diagnostics logger is imported first so any boot-time error is captured.

src/main.ts
import './diag/logger'
import './brain-ui'
import './events'
import { scene, initRendering } from './scene'
import { createRenderer } from './renderer'
import { loadAllAssets } from './loaders'
import { createBrain } from './brain'
import { initGui } from './gui'
import { runLoop } from './run'

The async boot() function then runs:

src/main.ts
async function boot(): Promise<void> {
const { renderer, backend } = await createRenderer()
initRendering(renderer, backend)

const assets = await loadAllAssets()

const brain = await createBrain(assets, renderer, backend)
;(window as any).neuralNet = brain
console.log(`[EXEPERT Brain] Travel backend: ${brain.travelBackend}`)

const loadingEl = document.getElementById('loading')
if (loadingEl) loadingEl.style.display = 'none'

scene.add(brain.meshComponents)

initGui(brain)
runLoop(brain)
}

Step by step

  1. Diagnostics logger (src/diag/logger.ts): imported first. It patches console.*, installs error/unhandledrejection handlers, and mounts the floating panel. See Diagnostics Logger.
  2. Renderer (src/renderer.ts): createRenderer() returns a WebGLRenderer and the 'webgl2' backend tag.
  3. Scene (src/scene.ts): initRendering() attaches the canvas, sets up the camera, OrbitControls, Stats, helpers, and the on-canvas status bar.
  4. Assets (src/loaders.ts): loadAllAssets() loads the brain OBJ mesh and the electric sprite texture, returning the extracted vertices.
  5. Brain (src/brain.ts): createBrain() initializes the WASM module, constructs the World from the vertices, and builds the neuron, axon, and particle geometry bound to WASM memory.
  6. Global handle: the Brain is stashed on window.neuralNet for the UI and any external integrations.
  7. Scene attach + GUI: the brain meshes are added to the scene and initGui() mounts the lil-gui panel.
  8. Run loop: runLoop(brain) starts the requestAnimationFrame loop.

If boot() rejects, the catch handler logs the error and hides the loading overlay so the failure is visible rather than stuck on a spinner.

Lazy-loaded modules

After boot() completes, several UI modules are loaded via requestIdleCallback (or setTimeout fallback) so they don't block the first frame:

src/main.ts
requestIdleCallback(() => import('./ui/search-box').then((m) => m.mountSearchBox()))
requestIdleCallback(() => import('./ui/prompt-playground').then((m) => {
return m.mountPromptPlayground()
}))

Each module is responsible for its own readiness checks. If a module's backend is unavailable (missing env vars, missing tables, etc.), it disables itself gracefully: no visible panel is mounted, and a console.warn is emitted.

ModuleMount guard
workbenchWires immediately; always responds. Activity-bar buttons switch views. Prompts view lazy-loads prompt-playground.
search-boxSkips if Typesense env vars are unset.
prompt-playgroundPassive mount skips if Supabase is unconfigured, the prompts table query fails, or the active project's prompt registry is empty. The Prompts activity-bar button opens the right-side panel on demand regardless.

Prompt Playground preflight

mountPromptPlayground() now renders its DOM shell immediately (including a loading spinner), then fetches listPrompts(getActiveProject().id). If the query fails (Supabase unconfigured, RLS error, network failure), an inline error message replaces the spinner. If the query returns zero rows and the call was not forceOpen, the playground closes itself. This means users always see a visual response: never a blank area.

A Prompts button in the far-left activity bar (#activityPromptsTrigger) allows users to open the playground on demand, even when the registry is empty. Clicking the button triggers openPromptsView() in workbench.ts, which lazy-imports prompt-playground.ts and calls mountPromptPlayground({ forceOpen: true }). This shows the right-side playground card with a + New button so the first prompt can be created. The trigger uses the currently active project from getActiveProject(), so prompt data is scoped to the project the user is viewing.