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 🙏

© 2024 – Pkg Stats / Ryan Hefner

ecstra

v0.0.2

Published

Fast & Flexible EntityComponentSystem (ECS) for JavaScript & TypeScript

Downloads

4

Readme

Ecstra

Build Status

🚧 Ecstra is a work-in-progress and might be unstable, use it at your own risks 🚧

Fast & Flexible EntityComponentSystem (ECS) for JavaScript and Typescript, available in browser and Node.js.

Get started with:

🔍 I am currently looking for people to help me to identify their needs in order to drive the development of this library further.

Philosophy

Created as 'Flecs', it's been renamed to 'Ecstra' to avoid duplicate

Ecstra (pronounced as "extra") is heavily based on Ecsy, but mixes concepts from other great ECS. It also share some concepts with Hecs.

My goals for the library is to keep it:

  • 💻 Framework Agnostic 💻
  • 🪶 Lightweight 🪶
  • ⚡ Fast ⚡
  • 🏋️ Robust 🏋️

The library will prioritize stability improvements over feature development.

Features

  • Easy To Use Query Language
  • System Grouping
  • System Topological Sorting
  • Automatic Component Registration
  • Component Properties Merging
  • System Queries Merging
  • TypeScript Decorators
    • For component properties
    • For system ordering and configuration
  • No Dependency

Install

Using npm:

npm install ecstra

Using yarn

yarn add ecstra

The library is distributed as an ES6 module, but also comes with two UMD builds:

  • fecs/umd/fecs.js → Development build with debug assertions
  • fecs/umd/fecs.min.js → Minified production build, without debug assertions

Usage Example

TypeScript

import {
  ComponentData,
  TagComponent,
  System,
  World,
  number,
  queries,
  ref
} from 'ecstra';

/**
 * Components definition.
 */

class Position2D extends ComponentData {
  @number()
  x!: number;
  @number()
  y!: number;
}

class FollowTarget extends ComponentData {
  @ref()
  target!: number;
  @number(1.0)
  speed!: number;
}

class PlayerTag extends TagComponent {}
class ZombieTag extends TagComponent {}

/**
 * Systems definition.
 */

@queries({
  // Select entities with all three components `ZombieTag`, `FollowTarget`, and
  // `Position2D`.
  zombies: [ZombieTag, FollowTarget, Position2D]
})
class ZombieFollowSystem extends System {

  execute(delta: number): void {
    this.queries.zombies.execute((entity) => {
      const { speed, target } = entity.read(FollowTarget);
      const position = entity.write(Position2D);
      const deltaX = target.x - position.x;
      const deltaY = target.y - position.y;
      const len = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
      if (len >= 0.00001) {
        position.x += speed * delta * (deltaX / len);
        position.y += speed * delta * (deltaY / len);
      }
    });
  }

}

const world = new World().register(ZombieFollowSystem);

// Creates a player entity.
const playerEntity = world.create().add(PlayerTag).add(Position2D);
const playerPosition = playerEntity.read();

// Creates 100 zombies at random positions with a `FollowTarget` component that
// will make them follow our player.
for (let i = 0; i < 100; ++i) {
  world.create()
    .add(ZombieTag)
    .add(Position2D, {
      x: Math.floor(Math.random() * 50.0) - 100.0,
      y: Math.floor(Math.random() * 50.0) - 100.0
    })
    .add(FollowTarget, { target: playerPosition })
}

// Runs the animation loop and execute all systems every frame.

let lastTime = 0.0;
function loop() {
  const currTime = performance.now();
  const deltaTime = currTime - lastTime;
  lastTime = currTime;
  world.execute(deltaTime);
  requestAnimationFrame(loop);
}
lastTime = performance.now();
loop();

JavaScript

import {
  ComponentData,
  TagComponent,
  NumberProp,
  RefProp,
  System,
  World
} from 'ecstra';

/**
 * Components definition.
 */

class Position2D extends ComponentData {}
Position2D.Properties = {
  x: NumberProp(),
  y: NumberProp()
};

class FollowTarget extends ComponentData {}
FollowTarget.Properties = {
  target: RefProp(),
  speed: NumberProp(1.0)
};

class PlayerTag extends TagComponent {}
class ZombieTag extends TagComponent {}

/**
 * Systems definition.
 */

class ZombieFollowSystem extends System {

  execute(delta) {
    this.queries.zombies.execute((entity) => {
      const { speed, target } = entity.read(FollowTarget);
      const position = entity.write(Position2D);
      const deltaX = target.x - position.x;
      const deltaY = target.y - position.y;
      const len = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
      if (len >= 0.00001) {
        position.x += speed * delta * (deltaX / len);
        position.y += speed * delta * (deltaY / len);
      }
    });
  }

}
ZombieFollowSystem.Queries = {
  // Select entities with all three components `ZombieTag`, `FollowTarget`, and
  // `Position2D`.
  zombies: [ZombieTag, FollowTarget, Position2D]
}

const world = new World().register(ZombieFollowSystem);

// Creates a player entity.
const playerEntity = world.create().add(PlayerTag).add(Position2D);
const playerPosition = playerEntity.read();

// Creates 100 zombies at random positions with a `FollowTarget` component that
// will make them follow our player.
for (let i = 0; i < 100; ++i) {
  world.create()
    .add(ZombieTag)
    .add(Position2D, {
      x: Math.floor(Math.random() * 50.0) - 100.0,
      y: Math.floor(Math.random() * 50.0) - 100.0
    })
    .add(FollowTarget, { target: playerPosition })
}

// Runs the animation loop and execute all systems every frame.

let lastTime = 0.0;
function loop() {
  const currTime = performance.now();
  const deltaTime = currTime - lastTime;
  lastTime = currTime;
  world.execute(deltaTime);
  requestAnimationFrame(loop);
}
lastTime = performance.now();
loop();

Running Examples

In order to try the examples, you need to build the library using:

yarn build # Alternatively, `yarn start` to watch the files

You can then start the examples web server using:

yarn example

TS Examples

TypeScript versions of the examples are available here. If you only want to see the example running, you can run the JS ones as they are identicial.

If you want to run the TypeScript examples themselves, please build the examples first:

yarn example:build # Alternatively, `yarn example:start` to watch the files

And then run the examples web server:

yarn example

Stable Version

The library is brand new and it's the perfect time for me to taylor it to match as much as possible most of the developer needs.

I want to open discussion about the following topics:

  • Deferred creation and removal of components
  • Deferred creation and removal of entities
  • Command buffers
  • Query system improvement
    • New selector (Modified? Removed?)
  • Is a StateComponent component needed?

Please feel free to reach out directly in the Github Issues or contact me on Twitter to discuss those topics.

Benchmarks

Coming soon.

Contributing

For detailed information about how to contribute, please have a look at the CONTRIBUTING.md guide.