[object Object]

← back to Wild Orbs Opus

chore: lint, refactor, v1.0.1 (session close)

40f3e992178e677b00fff394d821284de5614ba8 · 2026-09-09 09:16:20 -0700 · Steve Abrams

Refactor (behavior-preserving): extracted the duplicated wrapped-delta ->
force -> velocity step from six gravity-well / black-hole pull sites into one
applyPull() helper. Each site keeps its own constants and falloff formula; only
the shared 'divide by distance, add to velocity' step is centralized. An
independent reviewer had scored maintainability 6/10 citing exactly this.
(One site regroups (dx/d)*f*1.1 as (dx/d)*(f*1.1) — a last-ULP float
associativity difference, not a semantic one.)

Fix found by the close-out gate: the adaptive-resolution controller could not
shed pixels when it most needed to. Downscale and upscale shared one cooldown
counter, and a recovery step sets it to 300 frames — so a recent upscale locked
out an urgently-needed downscale for ~10 seconds, leaving the game pinned at
29fps while refusing to lower resolution. A clearly-dropping framerate now
bypasses the cooldown, and a downscale reseeds frameAvg just under the trigger
instead of down at a healthy value (which had forced the average to re-climb
from scratch after every step). Measured on a loaded machine: convergence to
the floor went 9s -> 6s and the settled framerate 49fps -> 66fps.

This was surfacing as an intermittent 56/57 'holds framerate under load'
failure that I had been attributing to machine load. Sampling renderScale once
a second showed it stuck at 0.95, which is unreachable by downscale steps and
therefore proved an upscale cooldown was blocking it. Suite is back to 57/57
with cross-engine + touch green.

Also removed four stray *-dtd.mjs probe files left in the repo by panel
subagents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XP13P5ZKs7oWJQ3pjbvpnG

Files touched

Diff

commit 40f3e992178e677b00fff394d821284de5614ba8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 09:16:20 2026 -0700

    chore: lint, refactor, v1.0.1 (session close)
    
    Refactor (behavior-preserving): extracted the duplicated wrapped-delta ->
    force -> velocity step from six gravity-well / black-hole pull sites into one
    applyPull() helper. Each site keeps its own constants and falloff formula; only
    the shared 'divide by distance, add to velocity' step is centralized. An
    independent reviewer had scored maintainability 6/10 citing exactly this.
    (One site regroups (dx/d)*f*1.1 as (dx/d)*(f*1.1) — a last-ULP float
    associativity difference, not a semantic one.)
    
    Fix found by the close-out gate: the adaptive-resolution controller could not
    shed pixels when it most needed to. Downscale and upscale shared one cooldown
    counter, and a recovery step sets it to 300 frames — so a recent upscale locked
    out an urgently-needed downscale for ~10 seconds, leaving the game pinned at
    29fps while refusing to lower resolution. A clearly-dropping framerate now
    bypasses the cooldown, and a downscale reseeds frameAvg just under the trigger
    instead of down at a healthy value (which had forced the average to re-climb
    from scratch after every step). Measured on a loaded machine: convergence to
    the floor went 9s -> 6s and the settled framerate 49fps -> 66fps.
    
    This was surfacing as an intermittent 56/57 'holds framerate under load'
    failure that I had been attributing to machine load. Sampling renderScale once
    a second showed it stuck at 0.95, which is unreachable by downscale steps and
    therefore proved an upscale cooldown was blocking it. Suite is back to 57/57
    with cross-engine + touch green.
    
    Also removed four stray *-dtd.mjs probe files left in the repo by panel
    subagents.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01XP13P5ZKs7oWJQ3pjbvpnG
---
 index.html   | 37 ++++++++++++++++++++++++++-----------
 package.json |  2 +-
 2 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/index.html b/index.html
index 40bda2a..7e5d727 100644
--- a/index.html
+++ b/index.html
@@ -232,10 +232,19 @@ function resize(){
 
 /** nudge the backing-store scale toward whatever holds ~60fps (with hysteresis) */
 function adaptResolution(){
-  if(scaleCooldown>0){ scaleCooldown--; return; }
+  // A recovery step sets a deliberately long cooldown so the resolution can't
+  // oscillate. But the cooldown is shared, so a recent upscale used to lock out
+  // an urgently-needed DOWNSCALE for ~10s — the game sat at 29fps refusing to
+  // shed pixels. A clearly-dropping framerate now bypasses the cooldown.
+  const urgent = frameAvg > 30;
+  if(scaleCooldown>0 && !urgent){ scaleCooldown--; return; }
   if(frameAvg > 23 && renderScale > 0.55){
     renderScale = Math.max(0.55, renderScale - 0.15);
-    scaleCooldown = 40; frameAvg = 16.7; applyBackingStore();
+    // Seed just UNDER the trigger, not down at a healthy 16.7: reseeding to a
+    // good value made the average re-climb from scratch after every step, so
+    // converging took ~9s of visible jank. Now a couple of still-slow frames
+    // step again immediately. Recovery stays deliberately slow (below).
+    scaleCooldown = 24; frameAvg = 21; applyBackingStore();
   } else if(frameAvg < 13.5 && renderScale < 1){
     renderScale = Math.min(1, renderScale + 0.1);
     scaleCooldown = 300; frameAvg = 16.7; applyBackingStore();
@@ -267,6 +276,15 @@ function wrapDelta(x1,y1,x2,y2){
   return [dx,dy];
 }
 function wrapDist(a,b){ const [dx,dy]=wrapDelta(a.x,a.y,b.x,b.y); return Math.hypot(dx,dy); }
+/** turn a wrapped delta + force magnitude into a velocity nudge on entity.
+ *  dx,dy,d must already be the wrapDelta/hypot to the field; f is the
+ *  caller's own (already strength+falloff scaled) force for this pull site —
+ *  every well/hole/entity pairing keeps its own formula, this just centralizes
+ *  the shared "divide by distance, scale by force, add to velocity" step. */
+function applyPull(entity, dx, dy, d, f){
+  entity.vx += (dx/d)*f;
+  entity.vy += (dy/d)*f;
+}
 
 /** Draw fn once per screen-wrap copy so entities never pop at the edges. */
 function wrapDraw(x,y,r,fn){
@@ -1555,8 +1573,7 @@ function update(){
       const [dx,dy] = wrapDelta(ship.x,ship.y,w.x,w.y);
       const d = Math.hypot(dx,dy);
       if(d < w.r && d>1){
-        const f = w.str * (1 - d/w.r) * 0.9;
-        ship.vx += (dx/d)*f; ship.vy += (dy/d)*f;
+        applyPull(ship, dx, dy, d, w.str * (1 - d/w.r) * 0.9);
       }
     }
     if(G.time%2===0){
@@ -1578,8 +1595,7 @@ function update(){
       const [dx,dy] = wrapDelta(ship.x,ship.y,h.x,h.y);
       const d = Math.hypot(dx,dy);
       if(d < h.pull && d>1){
-        const f = 0.85*(1 - d/h.pull);
-        ship.vx += (dx/d)*f*1.1; ship.vy += (dy/d)*f*1.1;
+        applyPull(ship, dx, dy, d, 0.85*(1 - d/h.pull)*1.1);
         if(d < h.r + ship.r*0.4) killShip();
       }
     }
@@ -1587,8 +1603,7 @@ function update(){
       const o=orbs[j];
       const [dx,dy]=wrapDelta(o.x,o.y,h.x,h.y); const d=Math.hypot(dx,dy)||1;
       if(d < h.pull){
-        const f = 0.7*(1-d/h.pull);
-        o.vx += (dx/d)*f; o.vy += (dy/d)*f;
+        applyPull(o, dx, dy, d, 0.7*(1-d/h.pull));
         if(d < h.r*0.9){
           h.eaten++;
           addScore(40,o.x,o.y);
@@ -1684,7 +1699,7 @@ function update(){
 
     for(const w of wells){
       const [dx,dy]=wrapDelta(o.x,o.y,w.x,w.y); const d=Math.hypot(dx,dy);
-      if(d<w.r && d>1){ const f=w.str*0.4*(1-d/w.r); o.vx+=(dx/d)*f; o.vy+=(dy/d)*f; }
+      if(d<w.r && d>1){ applyPull(o, dx, dy, d, w.str*0.4*(1-d/w.r)); }
     }
 
     // clamp runaway speed (wells + shockwaves + rubber boosts can stack)
@@ -1719,12 +1734,12 @@ function update(){
 
     for(const w of wells){
       const [dx,dy]=wrapDelta(b.x,b.y,w.x,w.y); const d=Math.hypot(dx,dy);
-      if(d<w.r && d>1){ const f=w.str*1.5*(1-d/w.r); b.vx+=(dx/d)*f; b.vy+=(dy/d)*f; }
+      if(d<w.r && d>1){ applyPull(b, dx, dy, d, w.str*1.5*(1-d/w.r)); }
     }
     for(const h of holes){
       const [dx,dy]=wrapDelta(b.x,b.y,h.x,h.y); const d=Math.hypot(dx,dy);
       if(d<h.pull && d>1){
-        const f=1.5*(1-d/h.pull); b.vx+=(dx/d)*f; b.vy+=(dy/d)*f;
+        applyPull(b, dx, dy, d, 1.5*(1-d/h.pull));
         if(d<h.r*0.8){ burst(h.x,h.y,4,'#c07bff',1.6,{life:16}); dead = true; break; }
       }
     }
diff --git a/package.json b/package.json
index 1682d33..639520b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "wild-orbs",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "description": "Asteroids, except every orb detonates into a different disaster. One self-contained HTML file.",
   "private": true,
   "scripts": {

← 4e055c9 Keep the neutral build comparator; leave the Qwen3 repo pris  ·  back to Wild Orbs Opus  ·  (newest)