# Filter Has No Kernel

*Task 1 of 5 · [Stream Compaction](https://gpu.rocks/learn/stream-compaction-0aed2e43.md) · GPU.js Learn*

On a CPU, filtering is four words: `data.filter(v => v >= 50)`.
Underneath it is a loop with a **moving write cursor** — every element that
passes gets pushed at wherever the last one left off:

```js
const kept = [];
for (const v of data) {
  if (v >= 50) kept.push(v);   // ← push() knows where the cursor is
}
```

That cursor is the problem. Thread 7 can tell you instantly whether
`data[7]` survives. It cannot tell you *where it goes*, because that
depends on how many of elements 0…6 survived — six other threads' business, none of which
thread 7 is allowed to ask about. An output position that depends on other threads is not
something a single kernel can compute, which is why there is no `filter`
kernel and never will be.

So compaction gets built out of pieces, and the first piece is the one part that
*is* perfectly independent: the **flag pass**. Turn the predicate into
a mask of 1s and 0s — a plain map, one thread per element, nobody talking to anybody. Run
it and look at what you get: the ones and zeros line up under the input, holes and all.
Flags say *who* survives. They move nothing.

## Goal

**Goal:** return `1` when this thread's sample is at or above
`this.constants.threshold`, and `0` when it is not.

## Requirements

- One thread per sample — `output: [64]`, no loops
- Return exactly `1` or `0`, never the sample value
- The predicate is *at or above*: a sample of exactly `50` survives

## Hint 1 — a predicate is just a map

Read your own element and compare it — nothing else. The comparison
`samples[this.thread.x] >= this.constants.threshold` is the whole
decision; all that is left is turning it into a number.

## Hint 2 — the one-liner

```js
return samples[this.thread.x] >= this.constants.threshold ? 1 : 0;
```

## Same idea elsewhere

Every compaction library on every platform starts here, and most of them let you
hand the mask in yourself: CUB's `DeviceSelect::Flagged` takes a flags array
beside the data, Thrust's `copy_if` takes the predicate and builds the same
mask internally, and a WebGPU pipeline writes it to a storage buffer with one dispatch.
The predicate pass is the cheap, embarrassingly parallel part — everything after it is
the interesting problem.

## Starter code

```js
// The flag pass: a map from "does this survive?" to 1 or 0.
const gpu = new GPU({ mode });

const flag = gpu.createKernel(function (samples) {
  // TODO: return 1 when this thread's sample is at or above
  // this.constants.threshold, and 0 when it is not.
  return samples[this.thread.x];
}, {
  output: [64],
  constants: { threshold: 50 },
});

const flags = await flag(samples);
console.log('samples:', samples.slice(0, 12).join(', '));
console.log('flags:  ', Array.from(flags).slice(0, 12).join(', '));
console.log('same 64 slots, holes and all — a flag says WHO survives, not WHERE it goes.');
```

---

Interactive version: https://gpu.rocks/learn/stream-compaction-0aed2e43/1

[Next task](https://gpu.rocks/learn/stream-compaction-0aed2e43/2.md)
