Events
Decoupled message bus facilitating strongly typed, event-driven communication across the React UI, 3D Engine Operators, and headless Systems.
Published: 7/10/2026
Aircada features a unified, highly optimized global event bus. By routing transient triggers, actions, and broadcasts through a typed event registry, Aircada prevents string hallucinations and ensures robust interoperability between 2D React overlays and 3D simulation loops.
Imperative System Usage (Internal Logic)#
Direct access to the event bus via AircadaContext (this.air.events) for custom systems or managers.
typescript
import { AircadaContext } from "@aircada/spec";
import { Events, EventPayloads } from "../registry/registry.generated";
@System({ name: "Custom Logic System", type: "custom_logic_system" })
export class CustomLogicSystem {
public initialize() {
// 1. Receiving: Add a listener
const unsub = this.air.events.addListener<EventPayloads[typeof Events.REQUEST_DOOR_OPEN]>(
Events.REQUEST_DOOR_OPEN,
(payload) => {
// 2. Transmitting: Invoke an event
this.air.events.invoke<EventPayloads[typeof Events.DOOR_OPENED]>(
Events.DOOR_OPENED,
{ doorId: payload.doorId, newAngle: 90 }
);
}
);
}
}warning
Always clean up your listeners during teardown to prevent memory leaks in the engine environment.
3D Operator Usage (@aircada/spec)#
Using @Input and @Output decorators to bind class methods and properties to the global event bus.
typescript
import { Operator, Context, FloatProperty, Input, Output, AirEvent, AircadaContext } from "@aircada/spec";
import { Events, EventPayloads } from "../registry/registry.generated";
@Operator({ name: "Door Logic" })
export class DoorOperator {
@Context() air!: AircadaContext;
@FloatProperty
doorAngle!: number;
// 1. Transmitting: Define an Output event
@Output({ name: "On Door Opened", emits: Events.DOOR_OPENED })
onDoorOpened = new AirEvent<EventPayloads[typeof Events.DOOR_OPENED]>();
// 2. Receiving: Define an Input method triggered by the global bus
@Input({ name: "Open Door", onEvent: Events.REQUEST_DOOR_OPEN })
handleOpenRequest(payload: EventPayloads[typeof Events.REQUEST_DOOR_OPEN]) {
// Perform logic and invoke output to broadcast back to global bus
this.onDoorOpened.invoke({ doorId: payload.doorId, newAngle: 90 });
}
}React UI Usage (@aircada/react)#
Using the
useAirEvent hook for both transmitting and receiving events in React components.Always strongly type the generic payload when using the hook.
typescript
import { useAirEvent } from '@aircada/react';
import { Events, EventPayloads } from './registry/registry.generated';
export function DoorController() {
// 1. Transmitting: Emitting an event to the global bus
const emitOpenDoor = useAirEvent<EventPayloads[typeof Events.REQUEST_DOOR_OPEN]>(
Events.REQUEST_DOOR_OPEN
);
// 2. Receiving: Listening to an event from the global bus
useAirEvent<EventPayloads[typeof Events.DOOR_OPENED]>(
Events.DOOR_OPENED,
(payload) => {
console.log(payload.doorId);
}
);
}The Source of Truth (registry.generated.ts)#
Defining event shapes in the auto-generated registry is highly recommended for type safety, though any string can technically be used as an event key.
Highly Recommended: Importing from the registry ensures valid bus addresses and provides full TypeScript intellisense for payloads.
typescript
// src/registry/registry.generated.ts
export const Events = {
REQUEST_DOOR_OPEN: "REQUEST_DOOR_OPEN",
DOOR_OPENED: "DOOR_OPENED"
} as const;
export interface EventPayloads {
[Events.REQUEST_DOOR_OPEN]: { doorId: string };
[Events.DOOR_OPENED]: { doorId: string; newAngle: number };
}





