---
name: quantum-forge
description: Quantum Forge framework reference — quantum operations, gates, entanglement, rendering, input. Use when writing game code, debugging WASM/imports, or understanding quantum mechanics in games.
allowed-tools: Read Glob Grep
user-invocable: true
argument-hint: [topic]
---

# Quantum Forge Reference

## Project Configuration

```!
node -e "
const fs = require('fs');
try {
  const p = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
  const deps = { ...p.dependencies, ...p.devDependencies };
  const core = deps['quantum-forge'] || deps['@quantum-native/quantum-forge'] || 'not installed';
  const engine = deps['quantum-forge-engine'] || deps['@quantum-native/quantum-forge-engine'] || 'not installed';
  const corePkg = deps['quantum-forge'] ? 'quantum-forge' : '@quantum-native/quantum-forge';
  const enginePkg = deps['quantum-forge-engine'] ? 'quantum-forge-engine' : '@quantum-native/quantum-forge-engine';
  console.log('Core: ' + corePkg + '@' + core);
  if (engine !== 'not installed') console.log('Engine: ' + enginePkg + '@' + engine);
  console.log('Type: ' + (p.type || 'commonjs'));
} catch { console.log('No package.json found'); }
" 2>/dev/null
```

## Initialization

```typescript
import { ensureLoaded } from "quantum-forge/quantum";
await ensureLoaded(); // MUST be called before any quantum operations
```

For the Qubit Edition (d2n20):

```typescript
import { useQuantumForgeBuild, ensureLoaded } from "quantum-forge/quantum";
useQuantumForgeBuild("qubit"); // before ensureLoaded()
await ensureLoaded();
```

### Vite Plugin

```typescript
import { quantumForgeVitePlugin } from "quantum-forge/vite-plugin";
export default defineConfig({
  plugins: [quantumForgeVitePlugin()],
  build: { rollupOptions: { external: [/quantum-forge-web-api/] } },
});
```

ESM note: if using `vite.config.js` (not `.ts`/`.mjs`), add `"type": "module"` to `package.json`.

## Core Concepts

Quantum Forge gives game objects real quantum state. The usage loop:

1. **Attach quantum properties** to things — each property is a qudit (d-dimensional quantum digit)
2. **Apply gates** to transform state without collapsing it
3. **Use predicated gates** to create entanglement — correlations between properties
4. **Measure** to collapse quantum state to a classical value (the game moment)

### Programmer's Mental Model

**Superposition** is a weighted collection of values. A qubit in equal superposition is `{|0⟩: 0.707, |1⟩: 0.707}`.

**Gates** are collection algorithms that transform every entry at once. `cycle` increments each value. `hadamard` creates equal superposition.

**Predicated gates** apply a filter: "if property A is |1⟩, flip property B." This creates entanglement — correlated entries in the collection.

The weights are **complex amplitudes**, not probabilities. When two entries with the same value meet, their amplitudes add. In phase = constructive interference (probability increases). Out of phase = destructive interference (probability cancels). Probability of a value = squared magnitude of its amplitude.

## QuantumPropertyManager

Extend this class to build your game's quantum system. It manages property lifecycle, pooling, and WASM access.

```typescript
import { QuantumPropertyManager, ensureLoaded } from "quantum-forge/quantum";

await ensureLoaded();

class QuantumRegistry extends QuantumPropertyManager {
  constructor(logger?: any) { super({ dimension: 2, logger }); }

  makeQuantum(id: string): void {
    const prop = this.acquireProperty();
    const m = this.getModule();
    m.cycle(prop);        // |0⟩ → |1⟩
    m.hadamard(prop);     // → 50/50 superposition
    this.setProperty(id, prop);
  }

  entangle(id1: string, id2: string): void {
    const p1 = this.getProperty(id1);
    const p2 = this.getProperty(id2);
    if (!p1 || !p2) return;
    this.getModule().i_swap(p1, p2, 0.5);
  }

  getProbability(id: string): number {
    const prop = this.getProperty(id);
    if (!prop) return 1.0;
    const results = this.getModule().probabilities([prop]);
    for (const r of results) {
      if (r.qudit_values[0] === 1) return r.probability;
    }
    return 0;
  }

  measure(id: string): number {
    const prop = this.getProperty(id);
    if (!prop) return 1;
    const [value] = this.getModule().measure_properties([prop]);
    this.deleteProperty(id);
    this.releaseProperty(prop, value); // reset to |0⟩, return to pool
    return value;
  }
}
```

### Dimension

| Dimension | States | Use Case |
|-----------|--------|----------|
| 2 (qubit) | `\|0⟩`, `\|1⟩` | Binary: exists/doesn't, alive/dead |
| 3 (qutrit) | `\|0⟩` – `\|2⟩` | Three-way: rock/paper/scissors |

Start with dimension 2 unless your design needs more. Shipped package supports up to 3.

## Gates

All gates are called via `getModule()`. Every gate accepts optional `predicates` for conditional execution (creates entanglement).

| Gate | Method | Dim | Description |
|------|--------|-----|-------------|
| Cycle | `m.cycle(prop, fraction?, preds?)` | Any | Cyclic permutation. `\|0⟩→\|1⟩→\|0⟩` for dim=2 (NOT gate) |
| Shift / X | `m.shift(prop, fraction?, preds?)` | Any | Inverse of cycle. Same as cycle for dim=2 |
| Hadamard | `m.hadamard(prop, fraction?, preds?)` | Any | Equal superposition. The main "make it quantum" gate |
| Inv. Hadamard | `m.inverse_hadamard(prop, preds?)` | Any | Reverse of hadamard |
| Clock / Z | `m.clock(prop, fraction, preds?)` | Any | Phase rotation. Invisible until interaction |
| Y | `m.y(prop, fraction?, preds?)` | **2 only** | Pauli Y. Throws if dimension != 2 |
| iSwap | `m.i_swap(p1, p2, fraction, preds?)` | Any | Anti-correlated entanglement |
| Swap | `m.swap(p1, p2, preds?)` | Any | Direct state exchange, no entanglement |
| Phase Rotate | `m.phase_rotate(preds, angle)` | Any | Phase rotation conditioned on predicates (required) |

**Fractional gates**: `fraction=1` is the full gate, `0.5` is the square root, `0.1` barely moves state. Key for gradual quantum effects.

### Predicates (Conditional Gates)

Every gate accepts predicates that condition it on other properties' states. **This is how entanglement works** — a gate that depends on another property's state correlates them.

```typescript
// CNOT: flip target only when control is |1⟩
m.shift(target, 1, [control.is(1)]);
// Result: (|00⟩ + |11⟩)/√2 — positively correlated

// Controlled Hadamard: target enters superposition conditionally
m.hadamard(target, 1, [control.is(1)]);

// Predicate types:
prop.is(value)      // true when property is |value⟩
prop.is_not(value)  // true when property is NOT |value⟩

// Multiple predicates are AND'd
m.shift(target, 1, [controlA.is(1), controlB.is(1)]);
```

For `PredicateSpec` objects (used in some APIs):

```typescript
import type { PredicateSpec } from "quantum-forge/quantum";
const specs: PredicateSpec[] = [
  { property: controlProp, value: 1, isEqual: true },
];
const wasmPreds = specs.map(s =>
  s.isEqual ? s.property.is(s.value) : s.property.is_not(s.value)
);
```

## Entanglement Patterns

### Entangle-Split (iSwap)

Object splits into anti-correlated pair. Exactly one is real.

```typescript
entangleSplit(originalId: string, newId: string): void {
  const prop1 = this.acquireProperty();
  const prop2 = this.acquireProperty();
  const m = this.getModule();
  m.cycle(prop1);                // |1⟩ (exists)
  m.i_swap(prop1, prop2, 0.5);  // entangle
  this.setProperty(originalId, prop1);
  this.setProperty(newId, prop2);
}
```

### Correlated Pair (CNOT)

Both match — both alive or both dead.

```typescript
createLinkedPair(id1: string, id2: string): void {
  const prop1 = this.acquireProperty();
  const prop2 = this.acquireProperty();
  const m = this.getModule();
  m.cycle(prop1);
  m.hadamard(prop1);
  m.shift(prop2, 1, [prop1.is(1)]); // CNOT
  this.setProperty(id1, prop1);
  this.setProperty(id2, prop2);
}
```

### Quantum-Split

Split an already-quantum object. Pooled properties preferred (no tensor product growth).

```typescript
quantumSplit(originalId: string, newId: string): boolean {
  const prop1 = this.getProperty(originalId);
  if (!prop1) return false;
  const prop2 = this.acquireProperty();
  try {
    this.getModule().i_swap(prop1, prop2, 0.5);
  } catch {
    this.releaseProperty(prop2, 0); // qudit limit
    return false;
  }
  this.setProperty(newId, prop2);
  return true;
}
```

### Overlap Entanglement

Objects that spatially overlap become entangled:

```typescript
this.getModule().i_swap(propA, propB, 0.5);
```

### Conditional Superposition

A control property determines whether a target enters superposition:

```typescript
m.hadamard(target, 1, [control.is(1)]);
```

### Choosing Between Mechanisms

| Mechanism | Correlation | Use |
|-----------|-------------|-----|
| Predicated shift (CNOT) | Positive — both match | Linked states: both alive or both dead |
| Predicated hadamard | Conditional superposition | One object's quantumness depends on another |
| `i_swap(0.5)` | Anti-correlated — exactly one | Object splits into two ghosts |
| `i_swap` + phase | Tunable bias | Player-influenced split probability |

## Measurement

Measurement collapses superposition to a definite value. Entangled partners collapse instantly too.

```typescript
const [value] = m.measure_properties([prop]); // probabilistic collapse
// Always pool after:
this.deleteProperty(id);
this.releaseProperty(prop, value);
```

### Batch Measurement

```typescript
const [va, vb, vc] = m.measure_properties([propA, propB, propC]);
```

### Reading State (No Collapse)

```typescript
// Probabilities — read-only, no state change
const results = m.probabilities([prop]);
// [{ probability: 0.5, qudit_values: [0] }, { probability: 0.5, qudit_values: [1] }]

// Reduced density matrix — phase and correlation info
const rdm = m.reduced_density_matrix([prop1, prop2]);
// Off-diagonal entries carry relative phase

// Measure predicate — projective measurement
const outcome = m.measure_predicate([prop1.is(1), prop2.is(0)]);
// 1 = satisfied, 0 = not

// Forced measurement — replay/save-load only
const [value] = m.forced_measure_properties([prop], [1]);
```

## Phase & Interference

Phase is invisible to measurement but changes how properties interact. Phase + iSwap = probability redistribution through interference.

```typescript
// Apply phase bias
m.clock(prop, 0.5);  // π/2 phase on |1⟩

// Later, entangle → phase redistributes probability
m.i_swap(propA, propB, 0.5);
// Same phase = constructive interference (probability concentrates)
// Opposite phase = destructive interference (probability cancels)
```

**Grover oracle** (walls/barriers): mark states with π phase, then diffuse with fractional Hadamard. Probability "bounces off" marked states.

```typescript
m.phase_rotate([hexProp.is(wallState)], Math.PI); // mark
m.hadamard(hexProp, 0.1); // diffuse
```

**Key insight:** Phase is the control knob that lets players influence quantum outcomes without directly choosing them. The player can't pick which ball exists, but they can bias the odds by applying phase before the next entanglement interaction.

### Visualizing Phase

Extract from the reduced density matrix:

```typescript
const rdm = m.reduced_density_matrix([propRef, propTarget]);
for (const entry of rdm) {
  if (entry.row_values[0] === 1 && entry.row_values[1] === 0 &&
      entry.col_values[0] === 0 && entry.col_values[1] === 1) {
    const phase = Math.atan2(entry.value.imag, entry.value.real);
    // Map to dial rotation, color hue, force magnitude, compass direction
  }
}
```

## Recording & Replay

```typescript
import { QuantumRecorder } from "quantum-forge/quantum";

const recorder = new QuantumRecorder(qpm);
recorder.startRecording();
// ... operations ...
const log = recorder.getLog(); // serializable JSON
// Save: localStorage.setItem("quantum-save", JSON.stringify(log));
// Load: recorder.replayLog(JSON.parse(saved)); // forced measurements
```

## Performance

**The one rule: pool your properties.** Measure → delete → release. Every time.

Shipped limits: Qutrit Edition has dimension 3, 12 max qudits. Qubit Edition has dimension 2, 20 max qudits.

| Active Properties | Performance |
|-------------------|-------------|
| 1–4 | No issues |
| 4–8 | Good with pooling |
| 8–12 | At the limit, aggressive pooling required |

**Most expensive operation**: tensor product (triggered by iSwap between properties in different shared states). Pooled properties avoid this — they're already in the shared state.

**Strategies**: aggressive pooling, limit concurrent quantum objects, use dimension 2 unless needed, separate registries for independent systems, batch measurements, throttle probability queries.

```typescript
// Monitor WASM memory
import { getWasmMemoryBytes } from "quantum-forge/quantum";
const mem = getWasmMemoryBytes(); // bytes, or null
```

## Engine

`Engine<TState>` is the state coordinator. Define your state type, expose mutations through `getHelpers()`.

```typescript
import { Engine } from "quantum-forge-engine/engine";

class MyEngine extends Engine<GameState> {
  constructor() { super(initialState); }
  getHelpers() {
    return {
      movePlayer: (dx: number, dy: number) => {
        const state = this.getState();
        this.setState({ ...state, player: { ...state.player, x: state.player.x + dx } });
      },
    };
  }
  reset() { this.setState(initialState); }
}
```

| Method | Description |
|--------|-------------|
| `getState()` | Current game state |
| `setState(state)` | Replace game state |
| `getHelpers()` | Object of state-mutating functions (override) |
| `reset()` | Reset to initial state (override) |

## Rendering

### PixiRenderer (WebGL/WebGPU)

```typescript
import { PixiRenderer, GameLoop } from "quantum-forge-engine/rendering";

class MyRenderer extends PixiRenderer {
  protected draw(state: GameState) {
    this.graphics.circle(state.player.x, state.player.y, 10).fill("#fff");
    this.drawText("score", `Score: ${state.score}`, 10, 10, { fill: "#fff", fontSize: 16 });
  }
}

await renderer.init(); // required before game loop
```

- `this.graphics` — PixiJS Graphics, cleared each frame
- `this.drawText(key, content, x, y, style?)` — managed text by key, no texture churn
- `this.stage` — PixiJS Container root
- `this.app` — PixiJS Application for sprites/containers

### CanvasRenderer (Canvas 2D)

```typescript
import { CanvasRenderer } from "quantum-forge-engine/rendering";

class MyRenderer extends CanvasRenderer {
  render(state: GameState) {
    this.clear("#000");
    // draw with this.ctx (CanvasRenderingContext2D)
  }
}
```

### GameLoop

```typescript
const loop = new GameLoop({
  update: (dt) => { input.poll(); /* update state */ },
  render: () => renderer.render(engine.getState()),
  targetFps: 60,
});
loop.start();
```

`dt` is in seconds (~0.0167 at 60 FPS).

**Rule:** renderers derive visuals from state. Never compute, cache, or decide in the renderer.

### Quantum Visualization

Map probability to visual properties (opacity, size, color):

```typescript
protected draw(state: GameState) {
  for (const ball of state.balls) {
    const alpha = ball.isQuantum ? ball.existenceProbability : 1.0;
    this.graphics.circle(ball.x, ball.y, ball.radius).fill({ color: ball.color, alpha });
  }
}
```

## Input

```typescript
import { InputManager, GamepadButtons, GamepadAxes } from "quantum-forge-engine/input";

const input = new InputManager({ logger });
input.bind("jump", { type: "key", code: "Space" }, { type: "gamepad-button", index: GamepadButtons.A });
input.bind("move-up", { type: "key", code: "KeyW" }, { type: "gamepad-axis", index: GamepadAxes.LeftStickY, direction: -1 });

// In update loop:
input.poll();
if (input.isActionJustPressed("jump")) { /* edge */ }
if (input.isActionDown("move-up")) { /* held */ }
const speed = input.getActionValue("move-up") * MAX_SPEED; // analog 0-1

// Event handlers
const unsub = input.on("pause", () => togglePause());

// Active device detection
const device = input.getActiveDevice(); // "keyboard" | "mouse" | "touch" | "gamepad"

// Always clean up
input.destroy();
```

**Source types:** `key`, `mouse-button`, `gamepad-button`, `gamepad-axis` (with direction), `touch-zone`, `touch-joystick`, `touch-gesture`

**Touch controls:** `input.addTouchZone({ name, x, y, w, h })` and `input.addJoystick({ name, zone, radius, deadZone, dynamic })`

**Local multiplayer:** `LocalMultiplayerManager` wraps multiple `InputManager` instances with automatic gamepad assignment.

## Package Exports

### Core (`quantum-forge`)

| Export | Key APIs |
|--------|----------|
| `./quantum` | `ensureLoaded`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild` |
| `./logging` | `Logger` |
| `./vite-plugin` | `quantumForgeVitePlugin` |

### Engine (`quantum-forge-engine`)

| Export | Key APIs |
|--------|----------|
| `./engine` | `Engine<TState>` |
| `./rendering` | `PixiRenderer`, `CanvasRenderer`, `GameLoop`, `Camera` |
| `./input` | `InputManager`, `LocalMultiplayerManager`, `GamepadButtons`, `GamepadAxes` |
| `./events` | `EventBus` |
| `./collision` | `SpatialGrid`, AABB/circle/point |
| `./audio` | `AudioManager` (Howler.js) |
| `./particles` | `ParticleSystem` |
| `./animation` | `AnimationSystem` |
| `./entities` | `EntityManager` |
| `./state-machine` | `StateMachine` |
| `./timer` | `TimerManager` |
| `./save` | `SaveManager` |
| `./scenes` | `SceneManager` |

Optional packages can be added via `npx quantum-forge-engine add-system`.

## Links

- Documentation: https://docs.quantum.dev
- Developer site: https://quantum.dev
- Discord: https://discord.gg/quantumforge
