# One Thread, One Sample

*Task 1 of 5 · [Sampling & Aliasing](https://gpu.rocks/learn/sampling-and-aliasing-ad14836c.md) · GPU.js Learn*

A signal, to a computer, is a list of numbers: `signal[i]` is how big the
thing was at moment *i*. Nothing else survives. Which moment is that? Sample
`i` happens at **t = i / sampleRate** seconds — and forgetting that
division is the single most common bug in this whole field, because the code still runs and
the answer is only wrong by a factor of the sample rate.

Building samples is the friendliest shape a GPU has: every sample is computed from its own
index and nothing else, so 256 threads do 256 independent sums. No neighbours, no ordering, no
coordination — the easiest possible parallel problem, and it produces the input every other
task in this module runs on.

Your `tones` input is three components, each an
`[amplitude, frequency in Hz, phase in radians]` triple. Add them up at this
thread's time:

```js
signal[i] = Σ  A · sin(2π · f · t + φ)        with t = i / sampleRate
```

Two things that bite. `Math.sin` counts *radians*, so a frequency in Hz
has to be multiplied by 2π before it goes in. And a real signal like this one is just a plain
`[n]` array of floats — the paired `output: [n, 2]` shape this track
uses for complex signals arrives later, with the DFT.

One boundary, stated plainly: your code runs in a Web Worker, which has no
`AudioContext`, no `OfflineAudioContext` and no
`navigator.mediaDevices`. Nothing here can record or play anything. Outside the
sandbox the samples would arrive like this, and the kernel would not know the difference:

```js
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const audio = new AudioContext({ sampleRate: 48000 });
const analyser = audio.createAnalyser();
audio.createMediaStreamSource(stream).connect(analyser);

const buffer = new Float32Array(analyser.fftSize);
analyser.getFloatTimeDomainData(buffer);   // ← 2048 real samples
kernel(buffer);
```

## Figures

- **sample 64 is not second 64 — the whole field runs on one division**

## Goal

**Goal:** fill 256 samples of a one-second signal — each thread turns its own
index into a time and sums the three components of `tones` at that time.

## Requirements

- Turn the thread index into seconds: `this.thread.x / this.constants.sampleRate`
- Loop over `this.constants.parts` components and accumulate `A * Math.sin(2 * Math.PI * f * t + phase)`
- Return the sum — `output: [256]`, one thread per sample

## Hint 1 — reading a component

`tones` is a 3×3 nested array. Component `c` is
`tones[c][0]` (amplitude), `tones[c][1]` (frequency, Hz) and
`tones[c][2]` (phase, radians).

## Hint 2 — the time of this sample

With `sampleRate` = 256, thread 0 is at t = 0 s, thread 64 is at
t = 0.25 s, and thread 255 is at t = 0.996 s:

```js
const t = this.thread.x / this.constants.sampleRate;
```

## Hint 3 — the loop body

```js
s += tones[c][0] * Math.sin(2 * Math.PI * tones[c][1] * t + tones[c][2]);
```

## Same idea elsewhere

Per-sample synthesis is the "embarrassingly parallel" case every platform opens with:
a CUDA kernel with one thread per sample, a WebGPU compute shader whose
`global_invocation_id.x` is the sample index, a Metal kernel over a 1D grid. It is
also what an `AudioWorkletProcessor` does per render quantum on the CPU — the same
arithmetic, 128 samples at a time instead of all of them at once.

## Starter code

```js
// 256 samples of a one-second signal. One thread per sample.
const gpu = new GPU({ mode });

const synth = gpu.createKernel(function (tones) {
  // TODO: turn this thread's INDEX into a time in seconds,
  //       then sum the three components of `tones` at that time.
  //   const t = this.thread.x / this.constants.sampleRate;
  return 0;
}, {
  output: [256],
  constants: { sampleRate: 256, parts: 3 },
});

const signal = synth(tones);
console.log('samples:', signal.length);
console.log('first four:', signal[0], signal[1], signal[2], signal[3]);
```

---

Interactive version: https://gpu.rocks/learn/sampling-and-aliasing-ad14836c/1

[Next task](https://gpu.rocks/learn/sampling-and-aliasing-ad14836c/2.md)
