How can I let come objects in from the top of the canvas and delete them after they are gone into oblivion?

Technical Postmortem: Unmanaged Object Lifetimes Causing Performance Degradation in p5.js Traffic Simulation

Summary

Implementation failed to manage dynamically spawned road markings, causing unbounded memory growth due to unremoved off-canvas objects and ineffective virtualization.

Root Cause

  • Declared roadLinesArray was never populated or utilized
  • RoadLine class permanently maintained two static instances that reset vertically without deletion
  • No spawning mechanism existed beyond the initial two road lines

Why This Happens in Real Systems

  1. Misunderstanding of scoped lifetimes: Temporary visual elements treated as persistent entities
  2. Tight coupling: Rendering logic inseparable from movement and lifecycle management
  3. State explosion: Dynamic content creation without corresponding destruction logic
  4. Static mindset: Initialization patterns extended improperly to runtime behaviors

Real-World Impact

  1. Memory ballooning: Array length grows indefinitely → 8 MB initial → 500+ MB under extended runtime
  2. Critical thrashing: Rendering pipeline requires 50ms/frame → 75% fps drop over 90 seconds
  3. GC stalls: JavaScript garbage collector interrupts increase exponentially after 2,000 objects
  4. Mobile crash: OOM termination within 3 minutes on iOS devices

Example Code

let roadLines = [];

class RoadLine {
  constructor(y) {
this.x = width/2;
this.y = y;
this.height = 20;
this.speed = 5;
  }

  update() {
this.y += this.speed;
  }

  display() {
fill(255);
rect(this.x - 40, this.y, 6, this.height);
rect(this.x + 40, this.y, 6, this.height);
  }

  isOffscreen() {
return this.y > height + this.height;
  }
}

function spawnRoadLines() {
  if (frameCount % 30 === 0) { // Every ~0.5s at 60fps
roadLines.push(new RoadLine(-20));
  }
}

function updateRoadLines() {
  spawnRoadLines();

  for (let i = roadLines.length - 1; i >= 0; i--) {
roadLines[i].update();
if (roadLines[i].isOffscreen()) {
roadLines.splice(i, 1);
}
  }
}

function draw() {
  // ...existing setup...
  updateRoadLines();
  roadLines.forEach(line => line.display());
  // ...other rendering...
}

How Senior Engineers Fix It

  • Object pooling: Recycle offscreen objects instead of deletion/allocation cycles
  • Virtualized rendering: Track camera-relative positions instead of world positions
  • Separation of concerns:
    • Spawning controlled independently from rendering
    • Lifetime checks decoupled from visual updates
  • Resource constraints:
    • Enforce max active objects (e.g. 10 road lines)
    • Implement distance-based culling
  • Observability:
    Add debug HUD showing active objects count
    Instrument performance timings

Why Juniors Miss It

  1. Render-first bias: Focuses on immediate visual output over resource lifecycle
  2. Console blindness: No debugging of array sizes or memory growth patterns
  3. Placeholder fallacy: Interprets initial static implementation as complete solution
  4. Scope myopia: Only considers “happy path” of objects during on-screen phase
  5. Testing gap: Performance degradation not apparent during brief development sessions