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

rngine

v0.11.3

Published

React Native 2D game engine powered by Nitro Modules

Readme

Rngine

A React Native game engine for building 2D games, powered by Nitro Modules for high-performance C++ game logic and rendered natively with Skia.

Installation

npm install rngine react-native-nitro-modules

react-native-nitro-modules is required as this library relies on Nitro Modules.

Usage

import { GameEngine, configure, updateEntity } from 'rngine';

configure({
  world: { tickRate: 60 },
  screen: { px: 400, py: 400, width: 800, height: 800, color: '#1a1a1a' },
  entities: [
    {
      id: 'player',
      px: 420,
      py: 420,
      color: '#0f0',
      shape: { radius: 20 },
    },
    {
      id: 'enemy_1',
      px: 120,
      py: 120,
      color: '#f00',
      shape: { width: 40, height: 40 },
      vx: 1000,
    },
    {
      id: 'enemy_2',
      px: 220,
      py: 220,
      color: '#f00',
      shape: { width: 40, height: 40 },
      vx: -1000,
    },
  ],
  systems: [
    {
      // runs every tick for all enemies
      entities: ['enemy'],
      onTick: ({ entities: enemies }) => {
        enemies.forEach((enemy) => {
          // reverse direction when reaching screen edges
          const halfWidth = enemy.shape.width / 2;
          if (enemy.px - halfWidth <= 0 || enemy.px + halfWidth >= 800) {
            updateEntity({ id: enemy.id, vx: -enemy.vx });
          }
        });
      },
    },
  ],
  paused: false,
});

export default function App() {
  return <GameEngine style={{ flex: 1 }} />;
}

Concepts

Entities are the objects in your game world. Each entity has a position, shape, and optional color/asset/velocity/acceleration/mass. See Entity.

Shapes describe an entity's collision and rendering geometry: either a rectangle ({ width, height }) or a circle ({ radius }).

Systems define your game logic. Each system optionally declares which entities it cares about via entities, which collision pairs to watch via collisions, or both. Systems run every tick receiving the resolved entities and any active collisions. See System.

Screen defines the viewport dimensions, position and optional background: color/asset. Entities outside the screen bounds are automatically clipped. See Screen

World defines global simulation settings: tickRate (game logic updates per second) and optional gravity (gx/gy, in game units per second²). See World

Rendering

Rngine renders natively with Skia: every frame is drawn directly in C++ on the game loop thread, with no bridge round-trip per frame. SVG, PNG, and Lottie animations are all supported as entity/screen assets, decoded and cached natively.

Physics

Entities with a mass are affected by the world's gravity (world.gx/gy) and participate in automatic collision resolution: overlapping entities are pushed apart and have their velocities updated via an impulse response. Entities without a mass (or mass: 0) are treated as immovable (infinite mass) when involved in a collision: useful for static geometry like a ground or walls.

Set isSensor: true on an entity to still receive collision events (Collision.depth, Collision.nx/ny) without any physical response: the entity won't be pushed, and other entities won't be pushed by it. This is useful for pickups, triggers, or grid-based games where you want to detect overlap yourself without physics involved.

Entity Querying

Entity ids use _ as a hierarchy separator, which enables prefix matching in systems and functions like update. Querying 'enemy' matches all entities whose id starts with enemy_. So enemy_1, enemy_magician_1, and enemy_warrior_2 are all returned. Querying 'enemy_magician' narrows it down to only magician entities. Querying 'enemy_magician_1' is an exact match.

This makes it easy to build entity groups naturally through naming. No extra configuration needed.

// matches enemy_1, enemy_magician_1, enemy_warrior_2
{ entities: ['enemy'], onTick: (entities) => {} }

// matches only enemy_magician_1, enemy_magician_2
{ entities: ['enemy_magician'], onTick: (entities) => {} }

// exact match
{ entities: ['enemy_magician_1'], onTick: (entities) => {} }

// collision detection between the player and all enemies
{ collisions: [{ a: 'player', b: 'enemy' }], onTick: (_, collisions) => {} }

Collision results include a normalized direction vector (nx/ny) pointing along the minimum separating axis, alongside the penetration depth: useful for resolving collisions manually or reacting to a specific direction of impact.

Assets

Assets (SVGs, PNGs, and Lottie animations) must be preloaded with loadAssets before being referenced by id, px/py, or any configure/spawn/update call. Loading happens off the JS thread and is cached. Calling loadAssets again with the same source is a no-op.

import { loadAssets } from 'rngine';

const assets = await loadAssets({
  player: require('./assets/player.svg'),
  explosion: require('./assets/explosion.json'), // Lottie
});

// assets.player and assets.explosion are numeric ids, ready to use as `asset` on any entity or the screen

API

configure(config)

Sets up the game engine. Call this before anything else.

| param | required | type | default | description | | ---------- | -------- | ---------- | ------- | ------------------------------------------------------- | | world | ✓ | World | - | Tick rate and optional gravity | | screen | ✓ | Screen | - | Screen dimensions, position, background color and asset | | entities | | Entity[] | [] | Initial entities to spawn | | systems | | System[] | [] | Systems to run each tick | | paused | | boolean | true | Whether to start paused |

loadAssets(assets)

Preloads one or more SVG, PNG or Lottie assets ahead of time. Takes an object mapping names to require(...) sources (SVG/PNG) or imported JSON (Lottie), and resolves to an object of the same shape with numeric asset ids.

spawn(entity | entity[])

Spawns one or more entities into the world. Skips duplicates by id.

despawn(id)

Removes the entity with the given id, or all entities matching the given prefix.

updateEntity(entityUpdate)

Fires one update, applied to entities matching the given id prefix from the world. Only the provided fields are changed.

updateEntities(entityUpdate[])

Fires multiple updates, each applied to entities matching the given id prefix from the world. Only the provided fields are changed.

screenUpdate(screenUpdate)

Updates the screen. Only the provided fields are changed.

pause()

Pauses the game loop. Systems stop running.

resume()

Resumes the game loop.

Contributing

License

MIT


Made with create-react-native-library