BINX FORGEALL YOU NEED IS AN IDEA.Support BINX ↗
PRACTICAL GUIDES & COMPARISONS · ORIGINAL SOURCE INCLUDED

Profile Game Stutter Before Asking AI to Rewrite It

Measure frame-time tails, separate rendering from simulation and make one evidence-based performance change.

Actual Signal Sweep gameplay: a playable BINX reference
Signal Sweep · Actual gameplay · Related playable reference

Thumbnail: an actual related playable BINX demo. It is a gameplay reference, not evidence that a save, inventory, network or native reverse-engineering integration has been demonstrated.

Difficulty: beginner to intermediate. Prerequisites: basic variables, functions and a test copy of your project. Reference: Forge workshop 1.0, browser ES modules and Node 24 checks. Engine-specific translation is a separate integration.

What you will learn

Measure frame-time tails, separate rendering from simulation and make one evidence-based performance change.

Download complete MIT workshop
  1. Extract into a new folder and run node check.mjs from that folder.
  2. For the browser games, run python3 -m http.server 8080 with Python 3 and open http://localhost:8080/index.html. Do not open modules with file://.
  3. Read the full setup instructions and the original code licence. Keep your existing game on a separate test branch.

The JavaScript behaviour checks and native C comparison can run independently of the browser. Ghidra UI, physical-phone and your engine integration are separate checks. This is a small reference, not a certification of your project.

Explore the related game idea · Start with Build a Game · Improve your existing game

Five practical steps

Profile Game Stutter Before Asking AI to Rewrite It: Record a reproducible workload → Read the slow frames → Find the expensive owner
Original implementation diagram · not gameplay or a tool screenshot
  1. Record a reproducible workload

    Choose one scene, target browser, resolution and fixed number of enemies. Warm up, then collect animation timestamp differences for thirty seconds. Frame duration includes more than your JavaScript execution; record simulation work separately with performance.now.

    Expected result: A baseline with hardware, scene and sample count.

    Check it: Repeat the baseline twice; large differences mean the workload is not controlled yet.

    frameSummaryObserve the inputs → check the state → compare the result
  2. Read the slow frames

    Run frameSummary on the captured positive durations. It returns a median and nearest-rank p95. A good average can hide frequent stalls; inspect the trace near a bad frame rather than treating one percentile as a full diagnosis.

    Expected result: Median and p95 in milliseconds, measured over the same interval.

    Check it: For [8,9,10,11,80], median is 10 and p95 is 80; a mean alone hides the shape.

  3. Find the expensive owner

    Use the browser Performance panel to separate script work, layout, painting and garbage collection. Search the code for repeated allocations, DOM writes, image decoding and unbounded object lists. Keep the existing working controller unless the trace actually implicates it.

    Expected result: One testable hypothesis connected to a visible trace.

    Check it: Compare the enemy-update section with no enemies and with the fixed baseline population.

  4. Change one thing

    Reuse already decoded images, remove an accidental duplicate frame loop or cache a repeated calculation, according to the evidence. Record the exact diff and repeat the same workload. Object pooling can help a proven allocation problem but adds reset bugs and complexity.

    Expected result: A before/after measurement with identical conditions.

    Check it: If p95 gets worse or outcomes differ, revert and investigate; do not report an invented improvement percentage.

  5. Protect correctness while tuning

    The workshop uses one frame owner and bounded fixed steps. It caps accumulated elapsed time after long stalls to avoid an unbounded catch-up loop; this deliberately drops some elapsed time. For physics-critical games, define the pause/catch-up policy explicitly.

    Expected result: Performance changes preserve control speed, collisions and game outcomes.

    Check it: Test 30/60/120Hz traces, hidden-tab resume, pause and repeated restart, plus the actual exported target.

Read and reuse the actual code

systems.mjs: complete source · Standalone behavioural checks · Shared browser runner and input. Original BINX Forge code, MIT; the package includes every required file and its notice.

Inspect complete systems.mjs
// Original BINX Forge practice code. MIT; see LICENSE.txt.
export function readSave(raw) {
  const s = JSON.parse(raw);
  if (!s || s.version !== 1 || !Number.isInteger(s.coins) || s.coins < 0 || s.coins > 9999 || !['room-a','room-b'].includes(s.room)) throw Error('Invalid save');
  return {version:1, coins:s.coins, room:s.room};
}
export function transfer(from, to, id, count, capacity=10) {
  if (!Number.isInteger(count) || count < 1 || !Number.isInteger(from[id]) || from[id] < count || Object.values(to).reduce((a,b)=>a+b,0)+count > capacity) return false;
  from[id]-=count; to[id]=(to[id]||0)+count; return true;
}
export function pointerInput() {
  const owners=new Map();
  return {press:(id,action)=>owners.set(id,action),release:id=>owners.delete(id),clear:()=>owners.clear(),held:action=>[...owners.values()].includes(action)};
}
export function enemyMode(distance, hp, cooldown) {
  if (hp<=0) return 'dead';
  if (cooldown>0) return 'recover';
  if (distance<24) return 'attack';
  return distance<180?'chase':'idle';
}
export function frameSummary(samples) {
  const s=samples.filter(Number.isFinite).filter(x=>x>=0).sort((a,b)=>a-b);
  if (!s.length) throw Error('No samples');
  return {median:s[Math.floor((s.length-1)*.5)],p95:s[Math.ceil(s.length*.95)-1],count:s.length};
}
export function fixedStep(clock, elapsed, update) {
  clock.carry+=Math.max(0,Math.min(.1,elapsed));
  while(clock.carry>=1/60){update(1/60);clock.carry-=1/60;}
}
// Owned protocol specimen: version:u8, coins:u16 little-endian, room:u8.
export function decodeRecord(bytes) {
  if(bytes.length!==4)throw Error('Expected four bytes');
  const v=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength);
  if(v.getUint8(0)!==1||v.getUint8(3)>1)throw Error('Unknown record');
  return {version:1,coins:v.getUint16(1,true),room:v.getUint8(3)};
}
export function applyInput(player, packet) {
  if(!packet||!Number.isInteger(packet.seq)||packet.seq<=player.seq||![-1,0,1].includes(packet.dx))return false;
  player.seq=packet.seq;player.x=Math.max(0,Math.min(100,player.x+packet.dx*2));return true;
}
export function jumpTrace(speed=300,gravity=900,dt=1/120) {
  let y=0,vy=-speed,t=0,peak=0;
  const rows=[{t,y,vy}];
  while(t<3){vy+=gravity*dt;y+=vy*dt;t+=dt;peak=Math.min(peak,y);rows.push({t,y,vy});if(y>=0)break;}
  return {rows,height:-peak,airtime:t};
}

Code rights: the included MIT notice permits use, modification and distribution, including commercial games and source products, with the copyright and licence notice. Added third-party files have their own terms. Read the official licence & usage terms.

Common failures and fixes

FPS looks high but the game stutters

Inspect tail durations and the trace, not only a rolling average.

Movement speeds up on fast screens

Use elapsed time or fixed updates; never add speed once per render.

Optimisation broke restarts

Reset pooled objects completely or remove pooling until its benefit is measured.

Make Your Game Better

Change one system after the baseline works. Keep the free reference and compare the same inputs before and after.

Optional building blocks from original creators

Inspect exact formats and editions on each resource page. Pay for a resource only when it fills an identified gap; the free code exercise needs no paid engine or pack.

Licensing summary, not legal advice. Before publishing or selling, check exact terms for finished-game use, reselling files and including files in a source/template product separately.

Learn at the original sources

Sources checked 11 October 2026. The numbered plan is original Forge instruction; linked documentation does not imply every engine or device has been tested.

Take this guide to your AI

Review and copy into your assistant. Nothing is sent automatically. These prompts include the complete focus source, full-package links, checks and compatibility limits.

Review a prompt or select its text manually.

Review build prompt
Review debug prompt
Review upgrade prompt

Read the complete guide and prompts as Markdown →

Keep learning

Browse every guide and recipe →