Skip to content
tezvyn:

Optimize a canvas with thousands of moving objects

Source: developer.mozilla.orgMediumHow cards are made

Optimize a canvas with thousands of moving objects

Tests GPU-bound vs CPU-bound bottleneck diagnosis. Strong answers: batch draw calls to cut JS-to-GPU overhead, and render to a smaller back buffer to reduce fill-rate cost.

What's really being asked

Whether you can identify the two most common WebGL pipeline bottlenecks when object count is high: excessive draw-call overhead and oversized back-buffer fill rate. The interviewer is checking if you know the difference between JavaScript-side work and GPU-side work, and whether you reach for pipeline-level fixes instead of generic language tricks.

The full answer

First, batch draw calls. The MDN guide explicitly recommends batching draw calls because each separate call incurs driver validation and state-tracking overhead. For thousands of objects, you should collapse individual submissions into fewer commands. One approach is merging geometry into a single interleaved vertex buffer so one drawArrays call renders many objects. Another is using ANGLE_instanced_arrays, which the reference notes is universally supported in WebGL 1, to issue one instanced draw call with per-instance transforms stored in a separate attribute buffer. Either technique cuts JavaScript-to-native overhead from thousands of API calls down to one or a handful.

Second, consider rendering to a smaller back buffer and manage devicePixelRatio carefully. The reference states that high-DPI rendering can be expensive and recommends considering a smaller back buffer. Drawing at full native resolution on a retina display quadruples fragment-shader work and memory bandwidth; instead render to a reduced-resolution framebuffer and let the browser upscale, or cap devicePixelRatio at a reasonable limit, to cut per-pixel cost. This is especially effective when objects overlap or use complex fragment shaders.

The mistakes people make

Proposing generic frame-rate limiters or event debouncing that ignore the GPU pipeline entirely. Suggesting Web Workers for canvas rendering is another red flag because the reference context is single-threaded WebGL and the main thread must issue commands. Recommending alpha:false is also wrong here; the reference warns that alpha:false can be expensive, so it is not an optimization to reach for blindly. Similarly, suggesting eager deletion of objects misses the point when the issue is active rendering load, not memory pressure.

What usually comes next

How would you measure whether the bottleneck is draw-call overhead versus fill rate? What are the trade-offs of reducing back-buffer resolution on text readability? How do you update buffer data each frame without causing pipeline stalls? When is instancing not applicable? Would you use requestAnimationFrame differently?

A concrete example

A dashboard with five thousand moving nodes. Without batching, each node triggers its own draw call and the CPU spends more time in the driver than the GPU spends drawing. By merging node geometry into one interleaved buffer and issuing a single drawArrays call, or using instanced arrays with a single drawArraysInstanced call, you reduce thousands of API calls to one. Then you halve the back-buffer dimensions so the fragment shader runs one quarter as many times. Together these typically cut frame time from thirty milliseconds to under five.

Interview question

A WebGL scene with thousands of moving objects shows high CPU time in driver validation. Which fix most directly targets this bottleneck?

  • a.Set alpha:false on the canvas context to skip compositing
  • b.Offload canvas commands to a Web Worker to free the main thread
  • c.Merge node geometry and submit a single batched draw callCorrect
  • d.Halve the back-buffer dimensions to reduce fragment-shader work
Why?

Merging geometry into a single buffer replaces thousands of API calls with one, eliminating per-call driver validation overhead. Halving the back-buffer is a fill-rate optimization that reduces GPU fragment work but does not address CPU-side draw-call overhead.

Just read this? Test yourself on what you have been reading.

Read the original → developer.mozilla.org

You just looked this up. Could you explain it out loud?

That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.

See open roles