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

@ujjwalvivek/tinyts

v0.2.1

Published

A tiny TypeScript-first 2D web game engine

Readme

TinyTS

API Quick Reference Examples Browser Engine API NPM

A tiny, fast, TypeScript-first 2D web game engine.

~88 KB minified, ~27 KB gzip - zero runtime dependencies. Lightweight. Performant. Reliable.

Quick Start

Minimal Boilerplate

Load the pre-bundled global build directly via a <script> tag. All engine exports are automatically mapped to the browser's global namespace:

<!DOCTYPE html>
<html lang="en">
    <head>
        ...
    </head>
    <body>
        <script src="dist/tinyts.js"></script>
        <script>
            const pos = vec2(100, 100);

            engineStart({
                size: { width: 480, height: 270 },
                pixelated: true,

                update(dt) {
                    if (keyDown("KeyD")) pos.x += 120 * dt;
                    if (keyDown("KeyA")) pos.x -= 120 * dt;
                },

                render() {
                    clear(Color.fromHSL(235, 0.45, 0.05));
                    drawRect(pos, vec2(16, 16), "#e94560");
                    drawText("USE A/D TO MOVE", vec2(240, 30), {
                        color: "#fff",
                        align: "center",
                        size: 16,
                    });
                },
            });
        </script>
    </body>
</html>

Modern ES Module Import

Install via npm and import components into your build system:

import { engineStart, vec2, clear, drawRect, Color } from "@ujjwalvivek/tinyts";

engineStart({
    size: { width: 640, height: 360 },
    render() {
        clear("#08080f");
        drawRect(vec2(320, 180), vec2(32, 32), "#f0c040");
    },
});

3. Scaffolding a TinyTS Game with Vite & TypeScript

Follow these steps to scaffold a project from scratch:

  1. Initialize your project:

    npm create vite@latest my-tinyts-game -- --template vanilla-ts
    cd my-tinyts-game
    npm install @ujjwalvivek/tinyts
  2. Configure your files:

    Update src/main.ts with this setup template:

    import { engineStart, vec2, clear, drawRect, keyDown } from "@ujjwalvivek/tinyts";
    
    const pos = vec2(100, 100);
    
    const engine = engineStart({
        size: { width: 480, height: 270 },
        pixelated: true,
        update(dt) {
            // Input tracking
            if (keyDown("KeyD") || keyDown("ArrowRight")) pos.x += 120 * dt;
            if (keyDown("KeyA") || keyDown("ArrowLeft")) pos.x -= 120 * dt;
        },
        render() {
            clear("#11111b"); // Clear background
            drawRect(pos, vec2(16, 16), "#89b4fa"); // Draw player
        },
    });
    
    // Mount engine canvas to index.html container
    const container = document.querySelector("#app");
    if (container) {
        container.appendChild(engine.canvasManager.canvas);
        if (engine.overlayCanvas) {
            container.appendChild(engine.overlayCanvas);
        }
    }

    Update index.html:

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>My TinyTS Game</title>
            <style>
                body {
                    margin: 0;
                    padding: 0;
                    background: #1e1e2e;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    width: 100vw;
                    height: 100vh;
                    overflow: hidden;
                }
                #app {
                    position: relative;
                    width: 100%;
                    height: 100%;
                }
                #app canvas {
                    position: absolute;
                    left: 50%;
                    top: 50%;
                    transform: translate(-50%, -50%);
                    max-width: 95%;
                    max-height: 95%;
                    aspect-ratio: 16/9;
                }
            </style>
        </head>
        <body>
            <div id="app"></div>
            <script type="module" src="/src/main.ts"></script>
        </body>
    </html>
  3. Run the development server:

    npm run dev

Engine Highlights

| Category | Description | | -------------------- | --------------------------------------------------------------------------- | | Overall Architecture | Singleton-free, instance-bound, clean module boundaries | | Game Loop | Fixed-timestep with accumulator, alpha interpolation, frame clamping | | Rendering | Unified interface, optional WebGPU, WebGL2 batcher, Canvas2D fallback | | Input | Bitmask state tracking, action mapping, gamepad, touch, keyboard and mouse | | Audio | Full ADSR synth, voice stealing, groups, spatial audio, sequencer | | Physics | Swept AABB, tilemap collision, spatial grid, verlet | | ECS | View caching, component pooling, hierarchy, serialization, event hooks | | Particles | Object pooling, emitter lifecycle, shapes, additive blending, prewarm | | API Ergonomics | Dead-simple global helpers plus full OOP access for power users |

TinyTS implements a highly disciplined engine design thats fun to work with:

  • Zero memory leaks on restart - engineStop() truly dismantles everything.
  • Embeddable - can run inside an iframe or component without polluting the host page.
  • Quad batching - 8192-quad batch buffer with pre-built index buffer
  • High-Performance WebGPU backend - opt in with webgpu: true; utilizes instanced rendering and a texture atlas. Falls back through WebGL2/Canvas2D when unavailable
  • State-cached uniforms - texture, shape, and shape params are tracked to avoid redundant GL calls
  • GPU-batched lines - drawLine() renders through the WebGL2 batcher as transformed quads
  • Canvas2D overlay - text and debug overlays use a synchronized Canvas2D layer
  • Sprite batching - rotated/flipped sprites are pre-transformed on CPU and batched, avoiding per-sprite draw calls
  • Renderer stats - getRendererStats() and the debug overlay expose draw calls, batch flushes, switches, quads, and overlay calls
  • Default font - ships and loads the MIT-licensed TinyTS font from dist/font/tinyTS.woff2
  • View caching with reactive updateEntityInCaches on add/remove
  • Component pooling via obtain() with init()/reset() hooks
  • Entity free list for ID recycling
  • Serialization via registerComponentType / serialize / deserialize
  • Event hooks via onAdded / onRemoved with unsubscribe handles
  • Voice pool with configurable max voices and voice stealing (oldest non-looping first)
  • Audio groups (sfx, music, ambient) with per-group volume
  • Spatial audio via playSoundAt with distance falloff and stereo panning
  • Sequencer for pattern-based music with MIDI-to-frequency conversion
  • Clip caching and AudioClip abstraction

Development & Build

# Ensure Node.js is installed

# Clone the repository
git clone https://github.com/ujjwalvivek/tinyts.git
cd tinyts
npm install        # Install package dependencies
npm run check      # Typecheck, run ES module / CJS builds, and execute tests
npm run docs       # Generate the API Documentation site locally
npm run serve      # Start a local HTTP utility server to test examples

Is This Reliable?

Does It "Just Work"?

| Scenario | Reliable? | | ------------------------------------------------------- | ---------------------------------------------- | | Drop a <script> tag, call engineStart(), draw stuff | ✅ Yes | | Use as ES module with bundler | ✅ Yes | | Pixel art games with integer scaling | ✅ Yes | | Smooth HD games with fractional scaling | ✅ Yes | | Start/stop/restart engine multiple times | ✅ Yes, zero leaks | | Mobile browser with touch | ✅ Yes, on-screen touch controls work | | Gamepad support | ✅ Yes, with deadzone | | WebGPU/WebGL2 not available | ✅ Yes, auto-falls back to Canvas2D | | Mix Canvas2D calls with engine rendering | ✅ Yes, getContext() exposes the raw context |

Runs Everywhere?

| Platform | Support | | ----------------------------- | ---------------------------------------------------------- | | Chrome / Edge (desktop) | ✅ Full WebGL2 + Canvas2D | | Firefox (desktop) | ✅ Full WebGL2 + Canvas2D | | Safari 15+ (desktop) | ✅ WebGL2 + Canvas2D | | Chrome / Firefox (Android) | ✅ WebGL2 + Canvas2D + Touch | | Safari (iOS 15+) | ✅ WebGL2 + Canvas2D + Touch | | Older browsers without WebGL2 | ✅ Canvas2D fallback | | Node.js (SSR / testing) | ✅ Headless (math, ECS, physics work; rendering needs DOM) |

Max Performance Out of the Box?

| Optimization | Present? | | --------------------------------------------------------- | -------- | | WebGL2 quad batching (single draw call per texture + shape) | ✅ | | High-Performance WebGPU backend (instanced rendering + texture atlas) | ✅ | | Pre-allocated batch Float32Array (no per-frame alloc) | ✅ | | Pre-built static index buffer | ✅ | | GPU-batched lines | ✅ | | Texture caching | ✅ | | State-cached uniforms (avoid redundant GL calls) | ✅ | | Renderer instrumentation counters | ✅ | | Object pooling (particles, ECS components) | ✅ | | SDF shapes (no per-circle geometry) | ✅ | | imageSmoothingEnabled = false for pixel art | ✅ | | Framerate-independent damping (Math.pow(damp, dt * 60)) | ✅ | | Spatial grid for broad-phase collision | ✅ |

License

MIT License.

TinyTS - Designed with zero bloat, maximum performance, and straightforward readability.