Task 4 of 5
Filters are not all averages. Give the window negative weights
and it starts measuring differences. The classic sharpen filter is a cross:
5 at the center, −1 at each direct neighbor. Where the image is
flat, the terms cancel to exactly the original value; where it changes, the difference
gets amplified — edges pop.
Sharpened values can overshoot right out of the 0–1 range, so this task computes on a
numeric luminance map (gray[y][x], one number per pixel)
and returns raw numbers you can inspect — no color clamping hiding the math.
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].
gray map — each cell becomes
5·center − left − right − up − down, with neighbor indexes clamped.0…this.constants.last5 * gray[y][x] minus the four clamped neighbor samplesgraphical: true, values may leave 0–1The weights sum to 1, so flat regions pass through unchanged:
5c − 4c = c. Everything the filter adds comes purely from
center-vs-neighbor differences.
let left = x - 1;
if (left < 0) left = 0;
— repeat for
right, up, down against
this.constants.last, then a single return with the five terms:
return 5 * gray[y][x] - gray[y][left] - gray[y][right]
- gray[up][x] - gray[down][x];This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.