Skip to content

Multi-File Imports

Open the editor to try multi-file projects live.

Split simulation logic across files with named ES import syntax. The bundler resolves dependencies at compile time.

Creating Files

Use + in the file explorer. One file is the entry (main.js by default — shown with an entry badge). New files are modules (isEntry: false). There is no UI to change which file is entry; keep your return in main.js.

Critical Limitation

Module files do not receive time, dt, state, w, or h. They run once at compile time. Export pure helpers and pass frame data from the entry:

javascript
// physics.js
function step(body, dt, g) {
  body.vy += g * dt;
  body.x += body.vx * dt;
  body.y += body.vy * dt;
}
module.exports = { step };

// main.js
import { step } from './physics';
step(state.ball, dt, 200);

Import Syntax

javascript
import { gravity, drag } from './physics';
import { Particle } from './particle';
FeatureSupportedExample
Named importsYesimport { foo, bar } from './mod'
Default importsNoimport foo from './mod'
Namespace importsNoimport * as foo from './mod'
Dynamic importsNoimport('mod')
export keyword in modulesNoUse module.exports
Relative ./Yes'./physics', './physics.js'
Parent ../NoFlat project namespace only
Multiline import statementsNoKeep imports on one line

Builtin ai module

ai is the one built-in module — no file needs to exist. It supports default, namespace, and named imports: import ai from 'ai', import * as ai from 'ai', or import { seek, flock } from 'ai'. It is not a global, so each file that uses it must import it. See the AI cookbook.

How Modules Work

Non-entry files compile like CommonJS:

javascript
new Function('exports', 'module', /* optional __modules */, code)

They run once per compile; module.exports is cached. They are not re-executed every frame.

javascript
// physics.js
function gravity(body, dt, g) {
  body.vy += g * dt;
}

function drag(body, dt, coeff) {
  body.vx *= (1 - coeff * dt);
  body.vy *= (1 - coeff * dt);
}

module.exports = { gravity, drag };

module and exports are injected — do not use ES export in module files.

Example: Particle Spawner

main.js (entry):

javascript
import { createParticle, updateParticles } from './physics';

if (!state.init) {
  state.particles = [];
  state.init = true;
}

if (state.clickX !== undefined) {
  for (let i = 0; i < 8; i++) {
    state.particles.push(createParticle(state.clickX, state.clickY));
  }
  state.clickX = undefined;
  state.clickY = undefined;
}

state.particles = updateParticles(state.particles, dt, w, h);

return {
  particles: state.particles.map((p) => ({
    x: p.x, y: p.y, r: 5, color: p.color,
  })),
  text: { count: state.particles.length },
};

physics.js:

javascript
import { COLORS } from './constants';

function createParticle(x, y) {
  return {
    x, y,
    vx: (Math.random() - 0.5) * 250,
    vy: -150 - Math.random() * 100,
    life: 1,
    decay: 0.2 + Math.random() * 0.5,
    color: COLORS[Math.floor(Math.random() * COLORS.length)],
  };
}

function updateParticles(particles, dt, w, h) {
  return particles.filter((p) => {
    p.vy += 200 * dt;
    p.x += p.vx * dt;
    p.y += p.vy * dt;
    p.life -= p.decay * dt;
    if (p.y > h - 20) { p.y = h - 20; p.vy *= -0.5; p.vx *= 0.8; }
    if (p.x > w - 10 || p.x < 10) p.vx *= -1;
    return p.life > 0;
  });
}

module.exports = { createParticle, updateParticles };

constants.js:

javascript
const COLORS = ['#8B5CF6', '#F59E0B', '#EF4444', '#22C55E', '#3B82F6'];
module.exports = { COLORS };

The bundler topo-sorts the graph and compiles modules before rewriting entry imports.

Circular Dependencies

Cycles throw at compile time. Move shared pieces into a third leaf module.

Import Resolution

SpecifierResolution
'./utils'File named utils or utils.js
'./utils.js'File named utils.js
'../utils'Not supported

Specifier must match the explorer file name (with or without .js).

Troubleshooting

ErrorCause
Module not foundSpecifier ≠ file name
Circular import detectedCycle in the graph
Error in module 'X'Syntax/runtime error while compiling that file
dt is not defined in a modulePass dt as an argument from the entry