Task 3 of 6

No Scatter Allowed

Here's the rule that shapes gpu.js kernels (and any fragment shader): a thread can read anywhere but can only write one place — its own cell, via return. There is no out[i + 1] = value here, because 4096 simultaneous writers into shared cells would be chaos (who wins? in what order?).

So the "push my value over there" plan — a scatter — must be turned inside out. Don't ask "where does my value go?"; ask "whose value lands in my cell?" — a gather. Try it on a rotation: every value moves one slot to the right, and the last wraps around to slot 0.

you can't push results to neighbours — pull what you need instead
Goal: rotate ring one slot to the right by gathering: each thread pulls the value that belongs in its cell.

Requirements

Hint 1 — invert the direction

If every value moves right by one, then the value in my cell came from my left: index this.thread.x - 1. The starter currently pulls from the right — that rotates the wrong way.

Hint 2 — wrapping without an if

Adding n before the modulo keeps the index positive:

(this.thread.x - 1 + this.constants.n) % this.constants.n

That turns -1 into 63 and leaves 1…63 alone.

Same idea elsewhere

Compute APIs relax this ban: CUDA, WebGPU and ROCm threads can store to any buffer address (scatter), and neighbours in a block cooperate through workgroup memory. But two threads storing to the same address is still a data race, and the escape hatch — atomics like atomicAdd — serializes threads and costs dearly. That's why GPU folklore compresses this lesson into four words: turn scatter into gather.

All tasks in Thinking in Parallel

  1. Map: One Thread, One Value
  2. Gather: Read Anywhere
  3. No Scatter Allowed
  4. Life on the Edge
  5. Smooth a Signal
  6. The Two-Pass Blur

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.