# Put It Together: Two Kernels

*Task 6 of 6 · [Data In, Data Out](https://gpu.rocks/learn/data-in-data-out-42b68d01.md) · GPU.js Learn*

Everything from this module in one pipeline. Kernel one reads the
`photo` and produces a 64×64 **luminance map** — pure numbers.
That result comes back to JavaScript, and you pass it straight into kernel two, a
**graphical** kernel that paints the map as a grayscale picture.

Array in → numbers out → array in again → pixels out. Data flowing *through*
kernels is the whole game (and **Pipelines & Textures** shows how to keep that
flow on the GPU).

**Array layout in gpu.js**
Image data comes in row-major: `image[y][x]` is the pixel in row *y*,
column *x*, and each pixel is an `[r, g, b, a]` array with channels from
0 to 1. Mind the inversion that catches everyone — sizes are given width-first
(`output: [width, height]`), but indexing runs row-first, so this thread's own
pixel is `image[this.thread.y][this.thread.x]`. Swap those two and you read the
transpose of your image. Three-dimensional data follows the same rule:
`output: [w, h, d]` is indexed `[z][y][x]`.

## Goal

**Goal:** finish both kernels — `luminance` returns
`0.299r + 0.587g + 0.114b` per pixel, and `paint` renders the map
as gray pixels with `this.color()`.

## Requirements

- Numeric kernel: read `photo[this.thread.y][this.thread.x]`, return the weighted luminance
- Graphical kernel: read this thread's value from `map`
- Paint it gray: `this.color(l, l, l, 1)`
- Feed the first kernel's result into the second (already wired up)

## Hint 1 — the luminance pass

Same lookup as before, but return a number:

```js
return 0.299 * pixel[0] + 0.587 * pixel[1] + 0.114 * pixel[2];
```

## Hint 2 — the paint pass

`map` is a plain 2D array of numbers, so

```js
const l = map[this.thread.y][this.thread.x];
this.color(l, l, l, 1);
```

## Same idea elsewhere

Multi-pass pipelines are the backbone of GPU work: render passes in graphics,
kernel launch chains in CUDA, encoder passes in WebGPU. The handoff you just did through
JavaScript is the slow version — pipelines (**Pipelines & Textures**) keep it
on-device.

## Starter code

```js
// Kernel 1 turns the photo into numbers. Kernel 2 turns numbers into pixels.
const gpu = new GPU({ mode });

const luminance = gpu.createKernel(function (photo) {
  // TODO: return perceptual luminance — 0.299 R + 0.587 G + 0.114 B
  return 0;
}, { output: [64, 64] });

const paint = gpu.createKernel(function (map) {
  // TODO: read this thread's value from map and paint it gray
  this.color(1, 0, 1, 1);
}, { output: [64, 64], graphical: true });

const map = await luminance(photo);
await paint(map);
render(paint.canvas);
```

---

Interactive version: https://gpu.rocks/learn/data-in-data-out-42b68d01/6

[Previous task](https://gpu.rocks/learn/data-in-data-out-42b68d01/5.md)
