# No Scatter Allowed

*Task 3 of 6 · [Thinking in Parallel](https://gpu.rocks/learn/thinking-in-parallel-c3876efb.md) · GPU.js Learn*

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.

## Figures

- **you can't push results to neighbours — pull what you need instead**

## Goal

**Goal:** rotate `ring` one slot to the right by gathering:
each thread pulls the value that belongs in its cell.

## Requirements

- No writes to other cells — express the shift purely as a read
- Thread `i` pulls from index `i − 1`
- Thread 0 wraps around and pulls the *last* element

## 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:

```js
(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*.

## Starter code

```js
// There is no out[i + 1] = value on a GPU. Threads only fill their OWN cell.
const gpu = new GPU({ mode });

// Wanted: every value moves one slot RIGHT, the last wraps to slot 0.
// You can't push your value right — so pull the right value in.
const rotate = gpu.createKernel(function (ring) {
  // TODO: this pulls from the wrong side — it rotates LEFT. Fix the
  // gather so each thread pulls the value that belongs in its cell.
  return ring[(this.thread.x + 1) % this.constants.n];
}, {
  output: [64],
  constants: { n: 64 },
});

const result = await rotate(ring);
console.log('ring[0] was', ring[0], '— it should now sit at result[1]:', result[1]);
```

---

Interactive version: https://gpu.rocks/learn/thinking-in-parallel-c3876efb/3

[Previous task](https://gpu.rocks/learn/thinking-in-parallel-c3876efb/2.md) · [Next task](https://gpu.rocks/learn/thinking-in-parallel-c3876efb/4.md)
