# One Tick of the Clock

*Task 4 of 5 · [N-Body Gravity](https://gpu.rocks/learn/n-body-gravity-5de47751.md) · GPU.js Learn*

Accelerations are just numbers until an integrator turns them into motion. The
simplest scheme that doesn't wreck orbits is **semi-implicit Euler**: update
the velocity *first*, then move the body with the *new* velocity —
`v′ = v + a·dt`, then `x′ = x + v′·dt`. Do it in the other order
(plain Euler) and orbits visibly spiral outward, gaining energy from nowhere.

Both updates are embarrassingly parallel — body *i* never looks at body
*j* — so they're two tiny kernels. Between them, the `[vx, vy]` pairs
come back to JavaScript and get unpacked into plain arrays for the next kernel. Clunky?
Yes. Instructive? Also yes — and **Pipelines & Textures** shows how to skip
the round trip.

## Goal

**Goal:** finish both kernels — `stepVel` returns
`[v + a·dt]` per component, `stepPos` returns
`[x + v·dt]` — and feed the position step the *new* velocities.

## Requirements

- `stepVel` returns `[vx + ax·dt, vy + ay·dt]` for its body
- `stepPos` returns `[x + vx·dt, y + vy·dt]` for its body
- The position step must receive the *updated* velocities (semi-implicit, already wired up)

## Hint 1 — the same index four times

Everything in both kernels is indexed by `this.thread.x`:
this body's velocity, this body's acceleration, this body's position.

## Hint 2 — the velocity kernel

```js
return [velX[this.thread.x] + accX[this.thread.x] * dt,
        velY[this.thread.x] + accY[this.thread.x] * dt];
```

— the position kernel is the
same shape with `pos` and `vel`.

## Same idea elsewhere

Splitting an integrator into per-buffer passes is exactly how GPU engines ship it:
WebGPU dispatches one compute pass per update with position/velocity buffers ping-ponging
between bind groups, and Metal encodes the same thing as back-to-back compute command
encoders. The math stays this small; the choreography is the product.

## Starter code

```js
// Numbers → motion. Semi-implicit Euler: update velocity FIRST,
// then move with the NEW velocity — it keeps orbits stable.
const gpu = new GPU({ mode });

const stepVel = gpu.createKernel(function (velX, velY, accX, accY, dt) {
  // TODO: return [new vx, new vy] — old velocity plus acceleration · dt
  return [velX[this.thread.x], velY[this.thread.x]];
}, { output: [64] });

const stepPos = gpu.createKernel(function (posX, posY, velX, velY, dt) {
  // TODO: return [new x, new y] — old position plus velocity · dt
  return [posX[this.thread.x], posY[this.thread.x]];
}, { output: [64] });

const DT = 0.01;
const newVel = await stepVel(velX, velY, accX, accY, DT);

// unpack the [vx, vy] pairs so the position kernel gets plain arrays
const newVelX = [];
const newVelY = [];
for (let i = 0; i < 64; i++) {
  newVelX.push(newVel[i][0]);
  newVelY.push(newVel[i][1]);
}

const newPos = await stepPos(posX, posY, newVelX, newVelY, DT);
console.log('body 0 moved to', newPos[0][0], newPos[0][1]);
```

---

Interactive version: https://gpu.rocks/learn/n-body-gravity-5de47751/4

[Previous task](https://gpu.rocks/learn/n-body-gravity-5de47751/3.md) · [Next task](https://gpu.rocks/learn/n-body-gravity-5de47751/5.md)
