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

miaoda-game-fixed-step-core

v0.3.2

Published

Engine-agnostic fixed-timestep coordinator for deterministic game logic: accumulator, catch-up limits, dropped-tick accounting, interpolation alpha, pause, reset, and manual stepping.

Readme

miaoda-game-fixed-step-core

Use this package to convert variable engine frame deltas into fixed simulation ticks for deterministic action, replay, physics ordering, and testable game loops. It also exposes an interpolation fraction for rendering.

Install and use

pnpm add miaoda-game-fixed-step-core
import { FixedStepper } from 'miaoda-game-fixed-step-core';

const clock = new FixedStepper({ tickRate: 60, maxCatchUpTicks: 5 });

function update(realDeltaSeconds: number) {
  const result = clock.advance(realDeltaSeconds, ({ index, dt }) => {
    input.commitForTick(index);
    world.tick(dt);
  });
  view.renderInterpolated(result.alpha);
}

advance receives seconds and may invoke zero or more fixed callbacks. Every callback receives dt = 1 / tickRate and a monotonically increasing zero-based tick index. alpha is only for visual interpolation; never feed it back into simulation rules.

Catch-up and pause behavior

  • overflowPolicy: 'discard' drops excess whole ticks beyond the catch-up cap while retaining fractional time. This is the default and prevents a runaway backlog.
  • overflowPolicy: 'carry' keeps the backlog for later frames when losing simulation time is unacceptable.
  • Pausing ignores incoming real time, so resume does not replay the paused duration.
  • step(count, callback) advances exact ticks for replay, debugging, and tests without changing the real-time accumulator.

If an engine adapter normally auto-updates a mechanic, disable that automatic update before driving it from this clock. Otherwise the mechanic advances twice per rendered frame.

The stepper owns only time accumulation and tick numbering. Input buffering, entity order, physics, rollback, networking, rendering, and time scale remain game-owned.

Live input per simulation tick

LogicalInputBuffer separates device polling from simulation consumption. Poll keyboard, gamepad, pointer, or an engine controls adapter once per host frame, then let zero or more fixed ticks consume the buffer:

const input = new LogicalInputBuffer();
const tape = new InputTapeRecorder({ tickRate: 60 });

function updateHostFrame(deltaSeconds: number) {
  input.sampleHostFrame({
    buttons: { jump: device.jumpDown },
    axes: { moveX: device.moveX },
  });

  clock.advance(deltaSeconds, ({ index, dt }) => {
    const frame = input.nextTick();
    tape.record(index, frame);
    world.tick(dt, frame);
  });
}

Button transitions are queued and each button consumes at most one transition per tick. A press and release received during one host frame therefore become a pressed tick followed by a released tick instead of disappearing. Held state continues on later ticks. Axes use the latest host-frame sample and repeat during catch-up ticks.

Paused clocks do not consume the buffer: the final held state, latest axes, and queued button transitions remain available on resume. Call input.clear() on focus loss or another explicit cancellation boundary when all pending and held input should become neutral.

Fixed-tick input recording

InputTapeRecorder records normalized logical input, not Phaser keys, Cocos events, DOM events, or wall-clock timestamps. Record inside the same fixed callback that advances gameplay:

const tape = new InputTapeRecorder({ tickRate: 60 });

clock.advance(deltaSeconds, ({ index, dt }) => {
  controls.update();
  tape.record(index, {
    buttons: { jump: controls.down('jump'), fire: controls.down('fire') },
    axes: { moveX: controls.axis('move').x, moveY: controls.axis('move').y },
  });
  world.tick(dt);
});

const saved = tape.snapshot; // versioned, JSON-safe, change-compressed
const replay = new InputTapePlayer(saved);
clock.step(saved.tickCount, () => world.tickFromInput(replay.next()!.frame));

Ticks must be consecutive. Buttons must be booleans and scalar axes must be finite values in [-1, 1]; malformed recordings and snapshots are rejected. deriveInputEdges(previous, current) reconstructs stable pressed/released transitions. This tape records what the player requested; authoritative state history, rollback, verification, and anti-cheat remain responsibilities of the command/session layer.