You're reading the 2.x docs. quantum-forge 3.0 is out: this page in 3.x / migration guide

Skip to content

Quantum Forge Setup ​

The WASM quantum simulator ships pre-built with the framework. Call ensureLoaded() before any quantum operations.

Quick Start ​

bash
npx quantum-forge-engine init my-game   # scaffolds project with setup included
cd my-game
npm run dev                             # WASM already configured

The scaffolder prompts for template, platforms, and edition. To run it without prompts, in CI or from an agent, pass every answer up front:

bash
npx -y quantum-forge-engine init my-game \
  --template starter --platforms web --edition qutrit --no-claude-skill

Initialization in Code ​

Always call ensureLoaded() before any quantum operation:

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

// Option 1: Background preload (recommended)
// Call after initial render for faster perceived startup
startBackgroundLoad(logger);

// Option 2: Blocking load before quantum operations
await ensureLoaded();

TIP

startBackgroundLoad() uses requestIdleCallback to begin loading the WASM module without blocking the main thread. Call it early (e.g., after your first render), then await ensureLoaded() later when you actually need quantum operations.

Vite Plugin ​

The framework includes a Vite plugin that serves WASM artifacts during development:

typescript
// vite.config.ts
import { quantumForgeVitePlugin } from "quantum-forge/vite-plugin";

export default defineConfig({
  plugins: [quantumForgeVitePlugin()],
  build: {
    rollupOptions: {
      external: [/quantum-forge-web-api/],
    },
  },
});

The plugin routes /quantum-forge/* requests to the built WASM files in dist/. No additional configuration needed.

Plugin options:

OptionDefaultDescription
wasmDir"dist"Directory containing WASM artifacts
servePath"/quantum-forge"URL path prefix for serving

Node and Headless Use ​

The same code runs in Node (tests, servers, agents) with no Vite plugin. On core releases after 2.7.0 the loader finds the WASM inside the installed package, and useQuantumForgeBuild("qubit") selects the variant the same way as in a page:

typescript
import { useQuantumForgeBuild, ensureLoaded, getModule } from "quantum-forge/quantum";

useQuantumForgeBuild("qubit"); // optional, before ensureLoaded()
await ensureLoaded();
const m = getModule();

On core 2.7.0 the default base path is the URL path /quantum-forge, which Node cannot import. Point the loader at the package's dist/ with a file URL instead, and do not call useQuantumForgeBuild() afterwards, because it would replace the path with /quantum-forge-qubit:

typescript
import { setWasmBasePath, ensureLoaded } from "quantum-forge/quantum";
import { createRequire } from "node:module";
import { pathToFileURL } from "node:url";
import path from "node:path";

const require = createRequire(import.meta.url);
const dist = path.dirname(require.resolve("quantum-forge/package.json")) + "/dist";
setWasmBasePath(pathToFileURL(dist).href);                          // Qutrit Edition
// setWasmBasePath(pathToFileURL(dist + "/quantum-forge-qubit").href); // Qubit Edition
await ensureLoaded();

Node 22 or newer.

Custom WASM Base Path ​

If you serve WASM files from a non-default location:

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

setWasmBasePath("/assets/wasm"); // must be called BEFORE ensureLoaded()
await ensureLoaded();

WASM Output Redirection ​

By default the WASM module prints diagnostic messages to console.log (stdout) and console.warn (stderr). You can redirect these by passing callbacks to QuantumForge.initialize():

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

// Low-level: call initialize() directly with options
const QF = getQuantumForge();
await QF.initialize({
  printErr: (text) => myLogger.warn(text),  // redirect stderr
  print: (text) => myLogger.debug(text),    // redirect stdout
});

The framework's startBackgroundLoad() / ensureLoaded() already routes printErr through the logger you pass in, so framework consumers get this for free:

typescript
startBackgroundLoad(logger); // WASM stderr → logger.warn("...", "QuantumForge/WASM")

TIP

This is useful for capturing WASM-level warnings in your game's logging system instead of losing them in the browser console.

Running headless (Node and Vitest) ​

ensureLoaded() resolves the WASM module against a URL path, defaulting to /quantum-forge, which is where the Vite plugin serves it during development. Outside a browser there is no dev server to answer that path, so the import fails. Point the loader at the files on disk first:

typescript
import { setWasmBasePath, ensureLoaded } from "quantum-forge/quantum";
import { join } from "node:path";

setWasmBasePath(join(process.cwd(), "node_modules/quantum-forge/dist"));
await ensureLoaded();

Use an absolute path. A relative one resolves against the module doing the dynamic import, not your working directory.

This is how CI, unit tests, and AI coding agents should exercise quantum code. Nothing about the simulator needs a browser, so a plain Node script is enough to check that a circuit does what you think it does:

typescript
// check-circuit.mts
import {
  setWasmBasePath,
  ensureLoaded,
  getModule,
  QuantumPropertyManager,
} from "quantum-forge/quantum";
import { join } from "node:path";

setWasmBasePath(join(process.cwd(), "node_modules/quantum-forge/dist"));
await ensureLoaded();

const manager = new QuantumPropertyManager({ dimension: 2 });
const m = getModule();

const prop = manager.acquireProperty();  // |0⟩
m.cycle(prop);                           // |0⟩ → |1⟩
m.hadamard(prop);                        // → (|0⟩ − |1⟩)/√2

console.log(m.probabilities([prop]));
// [{ probability: 0.5, qudit_values: [0] }, { probability: 0.5, qudit_values: [1] }]

const [value] = m.measure_properties([prop]);
console.log("measured", value);          // 0 or 1
manager.releaseProperty(prop, value);

In Vitest, put the setWasmBasePath() and await ensureLoaded() pair in a beforeAll. Both are idempotent across a suite: ensureLoaded() caches its promise, and calling setWasmBasePath() after the module has loaded has no effect.

Offline Support ​

Recent versions of the core package ship the service worker file itself. Projects created with npx quantum-forge-engine init get public/quantum-forge-sw.js written for them, so there is nothing to copy.

For a manual install, or a project scaffolded before the file existed, copy it out of the package into your public directory:

bash
cp node_modules/quantum-forge/quantum-forge-sw.js public/

Then register it once, after page load:

typescript
import { registerServiceWorker } from "quantum-forge/quantum";

await registerServiceWorker(); // defaults to "/quantum-forge-sw.js"

registerServiceWorker() returns null instead of throwing when the browser has no service worker support or registration fails, so it is safe to call unconditionally. Pass a different path as the only argument if you serve the file from somewhere other than the root.

The service worker uses a cache-first strategy for WASM artifacts. After the first load, the module works offline.

WARNING

The shipped quantum-forge-sw.js requires quantum-forge 2.6.5 or newer. Older packages did not include the file, and registerServiceWorker() on those versions only worked if you wrote the service worker yourself.

Editions ​

The published npm package ships two WASM build variants, called editions. You choose an edition when you run npx quantum-forge-engine init:

EditionDimensionsMax QuditsWASM Variant
Qutrit (default)2–312Default build — no extra config needed
Qubit2 only20Loads from /quantum-forge-qubit/

Qutrit Edition supports both qubits (dim=2) and qutrits (dim=3), giving you three-valued quantum states at the cost of a lower qudit ceiling. Qubit Edition locks dimension to 2 but raises the limit to 20 qubits, letting you run more quantum objects simultaneously.

Properties in separate (non-entangled) systems don't count against each other's limit. Proper pooling (measure → release → reuse) keeps the active count low. See Performance.

Switching Editions at Runtime ​

If you chose the Qubit edition during init, your generated main.ts already contains the required call. If you need to switch manually or add it to an existing project:

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

// Select the qubit variant — MUST be called before ensureLoaded()
useQuantumForgeBuild("qubit");
await ensureLoaded();

The Qutrit edition uses the default build and does not need useQuantumForgeBuild().

WARNING

useQuantumForgeBuild() must be called before ensureLoaded(). If the WASM module is already loaded, the call is ignored with a warning.

Querying the Active Edition ​

After loading, you can verify which build is active:

typescript
import { getMaxDimension, getMaxQudits } from "quantum-forge/quantum";

console.log(getMaxDimension()); // 3 (qutrit) or 2 (qubit)
console.log(getMaxQudits());    // 12 (qutrit) or 20 (qubit)

Future Versions

Future versions will support arbitrary qudit dimensions and higher qudit counts. The current limits are chosen to balance capability with WASM binary size and memory usage.

Runtime Queries ​

After loading, you can query the WASM module's configuration:

typescript
import { getVersion, getMaxDimension, getMaxQudits, getMaxStateSize } from "quantum-forge/quantum";

console.log(getVersion());        // e.g., "2.6.5"
console.log(getMaxDimension());   // 3 (qutrit) or 2 (qubit)
console.log(getMaxQudits());      // 12 (qutrit) or 20 (qubit)
console.log(getMaxStateSize());   // max basis amplitudes in one state vector

getWasmMemoryBytes() is also exported, but in current builds it always returns null. See Performance: WASM heap usage.

Attribution ​

The license requires visible attribution. getAttribution() returns the string to display, so you don't have to keep it in sync by hand:

typescript
import { getAttribution } from "quantum-forge/quantum";

console.log(getAttribution());
// "Powered by Quantum Forge — © Quantum Native — quantumnative.io"

Put it somewhere a player can find it: a credits screen, an about panel, a corner of the title screen. The loader also prints a badge to the browser console on startup, but that is a developer nicety, not the attribution.

Troubleshooting ​

"Quantum Forge not loaded" ​

Call await ensureLoaded() before using any quantum operations.

WASM files not found in production ​

Ensure WASM artifacts are copied to your public/static directory and setWasmBasePath() points to the correct URL path.

Powered by Quantum Forge