022
LVL 01 — WAVE DEFENDER SESSION 022 DAY 22

MORE FILTERS

🎫 PIXELCRAFT-010
Feature | 🔵 Easy | Priority: 🟠 High

Grayscale works. Now implement 3 more filters: brightness adjustment, contrast adjustment, and color inversion. Connect each to its toolbar button and settings slider.
CONCEPTS.UNLOCKED
ƒ
Functions
Reusable blocks of code: function name(params) { return result; }. Define once, call anywhere. The building block of all organized software.
Arrow Functions
const add = (a, b) => a + b; — shorter syntax for functions. Especially useful as arguments to other functions (callbacks).
Parameters & Return Values
Parameters are inputs. Return values are outputs. function double(x) { return x * 2; } — x goes in, x*2 comes out. Pure input→output transformations.
🚫
The DRY Principle
Don't Repeat Yourself. If you copy-paste the pixel loop 4 times, a bug fix in one requires fixing all four. Extract the loop into a shared utility function instead.
📎
Clamping
Pixel values must stay 0–255. Brightness +100 on a pixel at 200 = 300 → overflow! Math.min(255, Math.max(0, value)) keeps values in range.
🔌
Functions as Arguments
Pass a function to another function: applyFilter(ctx, w, h, grayscale). The loop doesn't know which filter — it just calls whatever function you give it.
HANDS-ON.TASKS
01
The Problem — Copy-Paste Temptation

You're about to copy-paste the pixel loop 3 more times. The senior dev stops you:

"If you find a bug in the loop, you'll fix it in one place and forget the other three."

02
Extract a Reusable Filter Function
function applyFilter(ctx, width, height, filterFn) { const imageData = ctx.getImageData(0, 0, width, height); const pixels = imageData.data; for (let i = 0; i < pixels.length; i += 4) { const [newR, newG, newB] = filterFn( pixels[i], pixels[i+1], pixels[i+2] ); pixels[i] = clamp(newR); pixels[i + 1] = clamp(newG); pixels[i + 2] = clamp(newB); } ctx.putImageData(imageData, 0, 0); } function clamp(value) { return Math.min(255, Math.max(0, Math.round(value))); }
The loop is written ONCE. The filter math is passed in as a function. Adding a new filter = writing 3 lines of math. No loop duplication.
03
Write All 4 Filter Functions
function grayscale(r, g, b) { const avg = (r + g + b) / 3; return [avg, avg, avg]; } function brightness(r, g, b, amount) { return [r + amount, g + amount, b + amount]; } function contrast(r, g, b, factor) { return [ ((r / 255 - 0.5) * factor + 0.5) * 255, ((g / 255 - 0.5) * factor + 0.5) * 255, ((b / 255 - 0.5) * factor + 0.5) * 255, ]; } function invert(r, g, b) { return [255 - r, 255 - g, 255 - b]; }
04
Wire to Buttons & Sliders
grayscaleBtn.addEventListener('click', () => { applyFilter(ctx, canvas.width, canvas.height, grayscale); }); brightnessSlider.addEventListener('input', (e) => { const amount = parseInt(e.target.value); applyFilter(ctx, canvas.width, canvas.height, (r, g, b) => brightness(r, g, b, amount) ); });
Notice the arrow function wrapping brightness — it captures the slider value and passes it to the filter. This pattern is called a closure.
05
Test All 4 Filters & Close Ticket

Test all 4 filters on the same image. Verify each produces the expected result.

git switch -c feature/PIXELCRAFT-010-more-filters git add src/scripts/ git commit -m "Implement brightness, contrast, invert filters with reusable applyFilter (PIXELCRAFT-010)" git push origin feature/PIXELCRAFT-010-more-filters # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Functions are the fundamental unit of abstraction.

applyFilter(data, fn) doesn't know which filter it's applying — it just loops over pixels and calls whatever function you give it. This separation of mechanism (the loop) from policy (the math) is the foundation of reusable software.

// The same principle at every scale:

applyFilter     → loop is mechanism
                  filter math is policy
Operating systems → abstract hardware
APIs             → abstract implementation
Databases        → abstract storage

// Abstraction is how humans manage complexity.
"Filter Pack"
[A] Add a 5th filter: "Warm" — increase R by 20, decrease B by 20. How many lines of code did it take? (Should be ~3.)
[B] Write a "posterize" filter that reduces each channel to 4 levels (0, 85, 170, 255) by rounding to the nearest level.
[C] Chain filters: apply grayscale THEN contrast THEN invert in sequence. Does the order matter? Test different orderings.
REF.MATERIAL
ARTICLE
Mozilla Developer Network
Complete guide to JavaScript functions: declarations, expressions, arrows, parameters, return values, scope, and closures.
FUNCTIONSMDNESSENTIAL
VIDEO
Web Dev Simplified
Functions, arrow functions, callbacks, and higher-order functions explained with clear visual examples.
FUNCTIONSCALLBACKS
ARTICLE
Wikipedia
The Don't Repeat Yourself principle explained: origins, rationale, and when DRY can go too far (premature abstraction).
DRYPRINCIPLES
VIDEO
Fireship
Arrow functions vs regular functions: syntax, this binding, and when to use each. Quick and practical.
ARROW FUNCTIONSQUICK
ARTICLE
javascript.info
Interactive tutorial on functions: declarations, expressions, default parameters, and returning values. Runnable code examples.
FUNCTIONSINTERACTIVE
// LEAVE EXCITED BECAUSE
PixelCraft has 4 working filters! And adding a 5th is now 3 lines of code. The reusable applyFilter function is elegant. This is what "clean code" means.