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

@claudiu-ceia/tick

v0.1.13

Published

Tiny runtime-scoped game playground toolkit.

Readme

@claudiu-ceia/tick

Tiny 2D game kitchen-sink for TypeScript + Bun.

Overview

tick is a small ECS-style runtime toolkit extracted from previous JS game experiments.

It currently includes:

  • ECS primitives (Entity, Component, EntityRegistry)
  • Ordered/fixed-step world scheduler (World + systems)
  • Input manager (keyboard + mouse state)
  • Collision shapes/entities + broadphase (SpatialHashBroadphase)
  • Lightweight physics (PhysicsBodyComponent, PhysicsSystem)
  • Render/scene utilities and a few debug helpers

Status

This is not a serious production engine right now (probably never).

It is mostly a personal playground for experimenting and learning. I publish it so I can reuse it across projects without copy-pasting.

Install

bun add @claudiu-ceia/tick

Quickstart

import {
  EcsRuntime,
  Entity,
  PhysicsBodyComponent,
  PhysicsSystem,
  RectangleCollisionShape,
  CollisionEntity,
  TransformComponent,
  Vector2D,
  World,
} from "@claudiu-ceia/tick";

class Box extends Entity {
  constructor() {
    super();
    this.addComponent(
      new TransformComponent({ position: new Vector2D(100, 80), rotation: 0, scale: 1 }),
    );
    this.addComponent(new PhysicsBodyComponent());
    this.addChild(new CollisionEntity(new RectangleCollisionShape(24, 24), "center"));
  }
}

const runtime = new EcsRuntime();
const world = new World({ runtime, fixedDeltaTime: 1 / 60 });
world.addSystem(new PhysicsSystem());

EcsRuntime.runWith(runtime, () => {
  const box = new Box();
  box.awake();
});

Examples

Run locally:

  • bun run example:bouncy-arena
  • bun run example:dino-runner
  • bun run example:collision-lab
  • bun run example:pixel-painter
  • bun run example:hud-viewport
  • bun run example:hud-layout
  • bun run example:hud-debug

Persistence

Persistence is runtime-scoped (runtime.store) and opt-in.

  1. Add static type on persisted entities/components.
  2. Declare state in components with this.atom(...) and this.ref(...).
  3. Register persisted entities with runtime.registerPersistedEntity(...).
  4. Save with runtime.store.snapshot(...) and restore with runtime.loadSnapshot(snapshot).

See examples/pixel-painter for a minimal autosave flow (localStorage + per-pixel save).

Assets (runtime-scoped)

Each EcsRuntime has an assets manager with scope-based lifecycle.

const scope = runtime.assets.createScope("main-scene");

await scope.loadImage("atlas", "/assets/runner.png");
await scope.loadAudio("jump", "/assets/jump.wav");
await scope.loadFont("pixel", "PixelFont", "url(/assets/pixel.woff2)");
await scope.loadSpriteSheetGrid("runner", "atlas", {
  frameWidth: 24,
  frameHeight: 24,
  count: 8,
  columns: 8,
});

const atlas = scope.getImage("atlas");
const runSheet = scope.getSpriteSheet("runner");

// On scene teardown:
scope.release();

HUD design space (responsive UI)

tick includes a HudViewport helper for resolution-independent HUD rendering.

import { HudViewport, RenderSystem, Vector2D } from "@claudiu-ceia/tick";

const hud = new HudViewport(new Vector2D(1920, 1080), "contain");
const renderSystem = new RenderSystem(canvasView, camera, runtime, hud);

// HUD components now render in 1920x1080 design units.
renderSystem.render();

For pointer input, convert DOM mouse coordinates (clientX/clientY) directly into HUD coordinates:

const clientPoint = runtime.input.getMousePos();
const hudPoint = hud.clientToHud(clientPoint, canvasElement);

HudViewport supports "contain", "cover", and "stretch" fit modes.

For HUD layout composition, use HudLayoutNodeComponent + HudDeckLayoutComponent / HudStackLayoutComponent:

const panel = new Entity();
panel.addComponent(
  new HudLayoutNodeComponent({
    width: 360,
    height: 140,
    anchor: "bottom-center",
    offset: { x: 0, y: -20 },
  }),
);
panel.addComponent(new HudDeckLayoutComponent({ padding: 10 }));

const row = new Entity();
row.addComponent(
  new HudLayoutNodeComponent({
    width: "95%", // percentage of parent frame
    height: 72,
    anchor: "bottom-center",
  }),
);
row.addComponent(new HudStackLayoutComponent({ direction: "row", gap: 10 }));

const slot = new Entity();
slot.addComponent(
  new HudLayoutNodeComponent({
    width: "fill", // split remaining main-axis space among fill siblings
    height: "fill", // in deck/cross-axis contexts: use full available size
    minWidth: 180,
    maxWidth: 260,
  }),
);

// Optional input hooks on a HUD entity
class AbilityInput extends HudInputComponent {
  protected override onPointerDown(e: HudInputEvent): void {
    e.stopPropagation();
  }

  protected override onKeyDown(e: HudInputEvent): void {
    // focused or global based on this.keyboardMode
  }
}

For a runnable demo that keeps the same HUD layout across multiple canvas resolutions:

bun run example:hud-viewport

For a larger RPG/MOBA-style HUD with nested deck/stack nodes:

bun run example:hud-layout

For HUD debugging, add HudLayoutDebugRenderComponent to any awake HUD entity. It draws resolved layout frames, anchor points, and optional labels for all HudLayoutNodeComponent nodes.

Minimal runnable debug demo:

bun run example:hud-debug

Development

bun install
bun run check

Useful scripts:

  • bun run typecheck
  • bun run lint
  • bun run format
  • bun run format:check
  • bun run test
  • bun run test:coverage
  • bun run check

License

MIT