npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oxecs/ecs

v0.1.0-alpha.6

Published

oxecs runtime and documentation skeleton

Readme

@oxecs/ecs

@oxecs/ecs is a data-oriented Entity Component System for JavaScript and TypeScript.

The runtime is the source of truth. Compiler passes, worker executors, and SharedArrayBuffer backends are acceleration layers, not prerequisites. The core must still work without them.

What you get

  • typed tokens for components, resources, events, and relations;
  • Bevy-style queries, systems, schedules, commands, and world access;
  • dense archetype storage for hot data;
  • SparseSet storage for direct entity-indexed data;
  • deterministic command playback and schedule validation;
  • explicit parallel-safety in the type surface;
  • referential cleanup for eid fields.

Install

npm install @oxecs/ecs

Quick Start

1. Create a world and run a query

import { createWorld, component, f32, query } from "@oxecs/ecs";

const Position = component({ x: f32, y: f32 });
const Velocity = component({ x: f32, y: f32 });

const world = createWorld();
world.spawn(
  [Position, { x: 0, y: 0 }],
  [Velocity, { x: 1, y: 2 }],
);

const moving = query(Position, Velocity);

world.query(moving).each((position, velocity) => {
  position.x += velocity.x;
  position.y += velocity.y;
});

2. Move the same logic into a system

import { component, f32, query, system } from "@oxecs/ecs";

const Position = component({ x: f32, y: f32 });
const Velocity = component({ x: f32, y: f32 });

export const Move = system([query(Position, Velocity)], (movement) => {
  movement.each((position, velocity) => {
    position.x += velocity.x;
    position.y += velocity.y;
  });
});

3. Run the same logic in a schedule

import { createWorld, createSchedule } from "@oxecs/ecs";
import { Move, Position, Velocity } from "./shared/movement";

const world = createWorld();
world.spawn([Position, { x: 0, y: 0 }], [Velocity, { x: 1, y: 2 }]);

createSchedule().add(Move).run(world);

The Usual Flow

  1. Define component schemas with component(...).
  2. Create a world with createWorld().
  3. Spawn entities with typed component values.
  4. Run ad-hoc query loops or move the same logic into systems.
  5. Add systems to a schedule and run it against the world.

Parallel Execution

Parallel execution is explicit and workload-aware.

  • Serial execution is the default.
  • Dense chunk workloads can be split across workers.
  • Small or sparse-heavy workloads may stay serial when that is cheaper.
  • The world behavior stays deterministic in both serial and parallel modes.

The query loop below:

world.query(moving).each((position, velocity) => {
  position.x += velocity.x;
  position.y += velocity.y;
});

is the serial form. It does not parallelize by itself.

Current host support

  • Node.js: supported.
  • Browser: not available yet.
  • React Native: not available yet.

If you need parallel execution today, treat Node.js as the only supported runtime target.

Optional Node parallel setup

You do not need this for normal ECS usage. Keep using schedule.run(world) when you want the simplest serial path.

When you want Node-backed parallel execution, move the reusable system into a worker-entry module and let the bootstrap plugin load it at worker start.

// src/shared/movement.ts
import { component, f32, query, system } from "@oxecs/ecs";

const Position = component({ x: f32, y: f32 });
const Velocity = component({ x: f32, y: f32 });

export const Move = system([query(Position, Velocity)], (movement) => {
  movement.each((position, velocity) => {
    position.x += velocity.x;
    position.y += velocity.y;
  });
});
export { Position, Velocity };
// src/workers/physics.ts
import { registerSystem } from "@oxecs/ecs";
import { Move } from "../shared/movement";

registerSystem("move", Move);
// src/main.ts
import { createWorld, createSchedule, SchedulerMode, ExecutionFallback } from "@oxecs/ecs";
import { createExecutorPlan, createNodeExecutor, createNodeRunner } from "@oxecs/ecs/extensions";
import { Move, Position, Velocity } from "./shared/movement";

const world = createWorld();
world.spawn([Position, { x: 0, y: 0 }], [Velocity, { x: 1, y: 2 }]);

const schedule = createSchedule().add(Move);
const plan = createExecutorPlan(schedule.plan(), SchedulerMode.Auto, ExecutionFallback.Serial);
const runner = createNodeRunner(createNodeExecutor());

runner.run(plan);

That is the general pattern:

  1. move the logic from world.query(...).each(...) into a reusable system(...);
  2. keep the system definition in a shared module;
  3. create a worker-entry module that imports the system and registers it with registerSystem(...);
  4. use the same system in createSchedule() on the main thread;
  5. build a plan with createExecutorPlan(...);
  6. execute the plan with createNodeRunner(createNodeExecutor()).

Bootstrap Plugin

@oxecs/ecs also ships an optional build-time unplugin helper for generating a worker bootstrap module.

Use it when you want the bundler to emit a Node worker bootstrap file from a set of ordinary worker-entry modules.

Those modules are just normal TypeScript files. They usually contain worker setup code, such as system registration, schedule construction, or other top-level initialization that should run when the worker starts.

In practice, a worker-entry module often imports reusable systems and registers them with registerSystem(...).

import { createBootstrapVitePlugin } from "@oxecs/ecs/compiler";

export default {
  plugins: [
    createBootstrapVitePlugin({
      imports: ["./src/workers/physics.ts", "./src/workers/render.ts"],
    }),
  ],
};

The plugin is a build-time convenience only. It does not replace the runtime executor.

Build and Verify

npm run check
npm run build
npm run test
npm run perf-gate

Publishing

The repository uses Git tags to drive publishing.

For an alpha release:

  1. Update package.json to the next prerelease version, for example 0.1.0-alpha.0.
  2. Commit the change.
  3. Create a tag that matches the version, for example v0.1.0-alpha.0.
  4. Push the tag to GitHub.

The GitHub Actions workflow publishes the package to npm with Trusted Publishing and creates a GitHub Release from the tag.

Contributing

See CONTRIBUTING.md for the local workflow, PR rules, and release process.

API Reference

License

Licensed under the MIT License.