about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2025-03-30 14:17:25 -0400
committerelioat <elioat@tilde.institute>2025-03-30 14:17:25 -0400
commitdc35422ab591a9240485da2e69d10020eb1de5ff (patch)
tree1d315ce4b369040b1fd7c71b70ebe2bec999061c
parent79e9336d5334c229092ff228e1146a6fdf33793c (diff)
downloadtour-dc35422ab591a9240485da2e69d10020eb1de5ff.tar.gz
*
-rw-r--r--js/leibovitz/balance.js32
-rw-r--r--js/leibovitz/blur.js59
-rw-r--r--js/leibovitz/color.js72
-rw-r--r--js/leibovitz/contrast.js36
-rw-r--r--js/leibovitz/dither.js143
-rw-r--r--js/leibovitz/leibovitz.js172
6 files changed, 357 insertions, 157 deletions
diff --git a/js/leibovitz/balance.js b/js/leibovitz/balance.js
index 6566176..73f60e8 100644
--- a/js/leibovitz/balance.js
+++ b/js/leibovitz/balance.js
@@ -1,11 +1,25 @@
-// BalanceManager handles white balance adjustments
+/**
+ * White balance management module implementing temperature-based color adjustment
+ * Uses the Observer pattern for state management and effect application
+ * Implements a non-linear temperature adjustment algorithm with RGB channel scaling
+ * Uses a static class pattern for state management
+ */
+
 class BalanceManager {
+    /**
+     * Initializes the balance manager and sets up UI controls
+     * Implements the Factory pattern for UI initialization
+     */
     static init() {
         this.balanceSlider = document.getElementById('balance-slider');
         this.balanceValue = document.getElementById('balance-value');
         this._setupEventListeners();
     }
 
+    /**
+     * Sets up event listeners for UI controls
+     * Implements the Observer pattern for state changes
+     */
     static _setupEventListeners() {
         this.balanceSlider.addEventListener('input', () => {
             const value = this.balanceSlider.value;
@@ -13,10 +27,26 @@ class BalanceManager {
         });
     }
 
+    /**
+     * Gets the current white balance temperature
+     * @returns {number} Current temperature in Kelvin (2000K-10000K)
+     */
     static getCurrentBalance() {
         return parseInt(this.balanceSlider.value);
     }
 
+    /**
+     * Applies white balance adjustment to an image
+     * Implements temperature-based RGB channel scaling with non-linear response
+     * @param {ImageData} imageData - Source image data
+     * @returns {ImageData} White balanced image data
+     * 
+     * Algorithm:
+     * 1. Convert temperature to ratio relative to neutral (6500K)
+     * 2. Apply non-linear scaling (0.2 factor) to red and blue channels
+     * 3. Warmer temps (<6500K) increase red, decrease blue
+     * 4. Cooler temps (>6500K) increase blue, decrease red
+     */
     static applyBalance(imageData) {
         const balance = this.getCurrentBalance();
         if (!balance || balance === 6500) return imageData; // 6500K is neutral
diff --git a/js/leibovitz/blur.js b/js/leibovitz/blur.js
index 08d1ec5..27fa480 100644
--- a/js/leibovitz/blur.js
+++ b/js/leibovitz/blur.js
@@ -1,5 +1,9 @@
-// Blur management module
-// Uses the Observer pattern to notify the main camera module of blur changes
+/**
+ * Blur management module implementing optimized box blur
+ * Uses the Observer pattern for state management and effect application
+ * Implements two-pass box blur algorithm with boundary detection
+ * Uses content-aware optimization for performance
+ */
 
 const BlurManager = {
     // Private state
@@ -8,14 +12,20 @@ const BlurManager = {
     _slider: null,
     _value: null,
 
-    // Initialize the blur manager
+    /**
+     * Initializes the blur manager and sets up UI controls
+     * Implements the Factory pattern for UI initialization
+     */
     init() {
         this._slider = document.getElementById('blur-slider');
         this._value = document.getElementById('blur-value');
         this._setupEventListeners();
     },
 
-    // Private methods
+    /**
+     * Sets up event listeners for UI controls
+     * Implements the Observer pattern for state changes
+     */
     _setupEventListeners() {
         this._slider.addEventListener('input', () => {
             const value = this._slider.value;
@@ -29,7 +39,11 @@ const BlurManager = {
         this._observers.forEach(observer => observer(this._currentBlur));
     },
 
-    // Public methods
+    /**
+     * Subscribes to blur state changes
+     * @param {Function} observer - Callback function for state changes
+     * @returns {Function} Unsubscribe function
+     */
     subscribe(observer) {
         this._observers.add(observer);
         return () => this._observers.delete(observer);
@@ -39,7 +53,14 @@ const BlurManager = {
         return this._currentBlur;
     },
 
-    // Apply Gaussian blur to an image
+    /**
+     * Applies optimized box blur to an image
+     * Implements two-pass blur with content-aware boundary detection
+     * Uses separate horizontal and vertical passes for performance
+     * @param {ImageData} imageData - Source image data
+     * @param {number} radius - Blur radius
+     * @returns {ImageData} Blurred image data
+     */
     applyBlur(imageData, radius) {
         if (!radius) return imageData;
 
@@ -132,28 +153,10 @@ const BlurManager = {
         return imageData;
     },
 
-    // Create a 1D Gaussian kernel
-    _createGaussianKernel(radius) {
-        const sigma = radius / 3;
-        const kernelSize = Math.ceil(radius * 2 + 1);
-        const kernel = new Array(kernelSize);
-        let sum = 0;
-
-        for (let i = 0; i < kernelSize; i++) {
-            const x = i - radius;
-            kernel[i] = Math.exp(-(x * x) / (2 * sigma * sigma));
-            sum += kernel[i];
-        }
-
-        // Normalize the kernel
-        for (let i = 0; i < kernelSize; i++) {
-            kernel[i] /= sum;
-        }
-
-        return kernel;
-    },
-
-    // Add reset method
+    /**
+     * Resets blur effect to default state
+     * Implements the Command pattern for state reset
+     */
     reset() {
         this._currentBlur = 0;
         this._slider.value = 0;
diff --git a/js/leibovitz/color.js b/js/leibovitz/color.js
index 4319a21..1438403 100644
--- a/js/leibovitz/color.js
+++ b/js/leibovitz/color.js
@@ -1,5 +1,9 @@
-// Color tint management module
-// Uses the Observer pattern to notify the main camera module of color tint changes
+/**
+ * Color tint management module implementing HSL-based color manipulation
+ * Uses the Observer pattern for state management and effect application
+ * Implements HSL color space transformation with circular interpolation
+ * Uses noise reduction and smooth blending for quality
+ */
 
 const ColorManager = {
     // Private state
@@ -7,12 +11,18 @@ const ColorManager = {
     _observers: new Set(),
     _colorInput: null,
 
-    // Initialize the color manager
+    /**
+     * Initializes the color manager and sets up UI controls
+     * Implements the Factory pattern for UI initialization
+     */
     init() {
         this._setupEventListeners();
     },
 
-    // Private methods
+    /**
+     * Sets up event listeners for UI controls
+     * Implements the Observer pattern for state changes
+     */
     _setupEventListeners() {
         this._colorInput = document.getElementById('color-tint');
         this._colorInput.addEventListener('input', (e) => {
@@ -47,7 +57,11 @@ const ColorManager = {
         this._observers.forEach(observer => observer(this._currentColor));
     },
 
-    // Public methods
+    /**
+     * Subscribes to color state changes
+     * @param {Function} observer - Callback function for state changes
+     * @returns {Function} Unsubscribe function
+     */
     subscribe(observer) {
         this._observers.add(observer);
         return () => this._observers.delete(observer);
@@ -57,7 +71,14 @@ const ColorManager = {
         return this._currentColor;
     },
 
-    // Apply color tint to an image using a more sophisticated LUT approach
+    /**
+     * Applies color tint to an image using HSL color space
+     * Implements circular interpolation for hue blending
+     * Uses noise reduction and smooth blending for quality
+     * @param {ImageData} imageData - Source image data
+     * @param {string} color - Hex color value
+     * @returns {ImageData} Tinted image data
+     */
     applyTint(imageData, color) {
         if (!color) return imageData;
 
@@ -99,7 +120,11 @@ const ColorManager = {
         return imageData;
     },
 
-    // Helper method to convert hex color to RGB
+    /**
+     * Converts hex color to RGB values
+     * @param {string} hex - Hex color string
+     * @returns {Array} RGB values [r, g, b]
+     */
     _hexToRgb(hex) {
         const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
         return result ? [
@@ -109,7 +134,13 @@ const ColorManager = {
         ] : null;
     },
 
-    // Convert RGB to HSL
+    /**
+     * Converts RGB to HSL color space
+     * @param {number} r - Red component
+     * @param {number} g - Green component
+     * @param {number} b - Blue component
+     * @returns {Array} HSL values [h, s, l]
+     */
     _rgbToHsl(r, g, b) {
         r /= 255;
         g /= 255;
@@ -136,7 +167,13 @@ const ColorManager = {
         return [h, s, l];
     },
 
-    // Convert HSL to RGB
+    /**
+     * Converts HSL to RGB color space
+     * @param {number} h - Hue component
+     * @param {number} s - Saturation component
+     * @param {number} l - Lightness component
+     * @returns {Array} RGB values [r, g, b]
+     */
     _hslToRgb(h, s, l) {
         let r, g, b;
         
@@ -163,7 +200,13 @@ const ColorManager = {
         return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
     },
 
-    // Blend two hue values (handles circular nature of hue)
+    /**
+     * Blends two hue values with circular interpolation
+     * @param {number} h1 - First hue value
+     * @param {number} h2 - Second hue value
+     * @param {number} factor - Blend factor
+     * @returns {number} Blended hue value
+     */
     _blendHue(h1, h2, factor) {
         const diff = h2 - h1;
         if (Math.abs(diff) > 0.5) {
@@ -176,7 +219,14 @@ const ColorManager = {
         return h1 + diff * factor;
     },
 
-    // Smooth blending with noise reduction
+    /**
+     * Smooth blending with noise reduction
+     * Uses cubic easing function for smooth transitions
+     * @param {number} v1 - First value
+     * @param {number} v2 - Second value
+     * @param {number} factor - Blend factor
+     * @returns {number} Blended value
+     */
     _smoothBlend(v1, v2, factor) {
         // Apply a smooth easing function
         const t = factor * factor * (3 - 2 * factor);
diff --git a/js/leibovitz/contrast.js b/js/leibovitz/contrast.js
index 5c4bc1c..01312ad 100644
--- a/js/leibovitz/contrast.js
+++ b/js/leibovitz/contrast.js
@@ -1,5 +1,8 @@
-// Contrast management module
-// Uses the Observer pattern to notify the main camera module of contrast changes
+/**
+ * Contrast management module implementing contrast adjustment
+ * Uses the Observer pattern for state management and effect application
+ * Implements linear contrast adjustment algorithm
+ */
 
 const ContrastManager = {
     // Private state
@@ -7,12 +10,18 @@ const ContrastManager = {
     _observers: new Set(),
     _slider: null,
 
-    // Initialize the contrast manager
+    /**
+     * Initializes the contrast manager and sets up UI controls
+     * Implements the Factory pattern for UI initialization
+     */
     init() {
         this._setupEventListeners();
     },
 
-    // Private methods
+    /**
+     * Sets up event listeners for UI controls
+     * Implements the Observer pattern for state changes
+     */
     _setupEventListeners() {
         this._slider = document.getElementById('contrast-slider');
         this._slider.addEventListener('input', (e) => {
@@ -26,7 +35,11 @@ const ContrastManager = {
         this._observers.forEach(observer => observer(this._currentContrast));
     },
 
-    // Public methods
+    /**
+     * Subscribes to contrast state changes
+     * @param {Function} observer - Callback function for state changes
+     * @returns {Function} Unsubscribe function
+     */
     subscribe(observer) {
         this._observers.add(observer);
         return () => this._observers.delete(observer);
@@ -36,7 +49,13 @@ const ContrastManager = {
         return this._currentContrast;
     },
 
-    // Apply contrast to an image
+    /**
+     * Applies contrast adjustment to an image
+     * Implements linear contrast adjustment algorithm
+     * @param {ImageData} imageData - Source image data
+     * @param {number} contrast - Contrast value
+     * @returns {ImageData} Contrasted image data
+     */
     applyContrast(imageData, contrast) {
         if (!contrast || contrast === 1.0) return imageData;
 
@@ -54,7 +73,10 @@ const ContrastManager = {
         return imageData;
     },
 
-    // Add reset method
+    /**
+     * Resets contrast effect to default state
+     * Implements the Command pattern for state reset
+     */
     reset() {
         this._currentContrast = 1.0;
         this._slider.value = 0; // Reset slider to middle position
diff --git a/js/leibovitz/dither.js b/js/leibovitz/dither.js
index 90d6325..3689354 100644
--- a/js/leibovitz/dither.js
+++ b/js/leibovitz/dither.js
@@ -1,5 +1,9 @@
-// Dithering management module
-// Uses the Observer pattern to notify the main camera module of dithering changes
+/**
+ * Dithering management module implementing various dithering algorithms
+ * Uses the Observer pattern for state management and effect application
+ * Implements block-based processing for performance optimization
+ * Uses the Strategy pattern for algorithm selection
+ */
 
 const DitherManager = {
     // Private state
@@ -9,13 +13,19 @@ const DitherManager = {
     _pixelSizeControl: null,
     currentBlockSize: 4,
 
-    // Initialize the dither manager
+    /**
+     * Initializes the dither manager and sets up UI controls
+     * Implements the Factory pattern for UI initialization
+     */
     init() {
         this._setupEventListeners();
         this._pixelSizeControl = document.getElementById('pixel-size-control');
     },
 
-    // Private methods
+    /**
+     * Sets up event listeners for UI controls
+     * Implements the Observer pattern for state changes
+     */
     _setupEventListeners() {
         this._modeSelect = document.getElementById('dither-select');
         this._modeSelect.addEventListener('change', (e) => {
@@ -43,7 +53,11 @@ const DitherManager = {
         this._observers.forEach(observer => observer(this._currentMode));
     },
 
-    // Public methods
+    /**
+     * Subscribes to dithering state changes
+     * @param {Function} observer - Callback function for state changes
+     * @returns {Function} Unsubscribe function
+     */
     subscribe(observer) {
         this._observers.add(observer);
         return () => this._observers.delete(observer);
@@ -53,7 +67,13 @@ const DitherManager = {
         return this._currentMode;
     },
 
-    // Apply dithering to an image
+    /**
+     * Applies selected dithering algorithm to image data
+     * Implements the Strategy pattern for algorithm selection
+     * @param {ImageData} imageData - Source image data
+     * @param {string} mode - Selected dithering algorithm
+     * @returns {ImageData} Processed image data
+     */
     applyDither(imageData, mode) {
         if (!mode || mode === 'none') return imageData;
 
@@ -75,20 +95,34 @@ const DitherManager = {
         }
     },
 
-    // Quantize a value to create chunkier output
+    /**
+     * Quantizes a value to create chunkier output
+     * Implements the Strategy pattern for quantization
+     * @param {number} value - Input value
+     * @param {number} levels - Number of quantization levels
+     * @returns {number} Quantized value
+     */
     _quantize(value, levels = 4) {
         const step = 255 / (levels - 1);
         return Math.round(value / step) * step;
     },
 
-    // Floyd-Steinberg dithering
+    /**
+     * Applies Floyd-Steinberg dithering algorithm
+     * Implements error diffusion with block-based processing
+     * Uses a 4x4 error distribution pattern for smoother results
+     * @param {Uint8ClampedArray} data - Image data
+     * @param {number} width - Image width
+     * @param {number} height - Image height
+     * @returns {ImageData} Dithered image data
+     */
     _floydSteinbergDither(data, width, height) {
         const newData = new Uint8ClampedArray(data);
         const threshold = 128;
         const levels = 4;
         const blockSize = this.currentBlockSize;
 
-        // Process in blocks
+        // Process in blocks for performance
         for (let y = 0; y < height; y += blockSize) {
             for (let x = 0; x < width; x += blockSize) {
                 // Calculate block average
@@ -143,7 +177,17 @@ const DitherManager = {
         return new ImageData(newData, width, height);
     },
 
-    // Helper method to distribute error to blocks
+    /**
+     * Distributes error to neighboring blocks
+     * @param {Uint8ClampedArray} data - Image data
+     * @param {number} x - X coordinate
+     * @param {number} y - Y coordinate
+     * @param {number} channel - Color channel
+     * @param {number} error - Error value to distribute
+     * @param {number} width - Image width
+     * @param {number} blockSize - Size of processing blocks
+     * @param {number} height - Image height
+     */
     _distributeBlockError(data, x, y, channel, error, width, blockSize, height) {
         for (let by = 0; by < blockSize && y + by < height; by++) {
             for (let bx = 0; bx < blockSize && x + bx < width; bx++) {
@@ -153,7 +197,15 @@ const DitherManager = {
         }
     },
 
-    // Ordered dithering (Bayer matrix)
+    /**
+     * Applies ordered dithering using a Bayer matrix
+     * Implements threshold-based dithering with block processing
+     * Uses a 4x4 Bayer matrix pattern for structured dithering
+     * @param {Uint8ClampedArray} data - Image data
+     * @param {number} width - Image width
+     * @param {number} height - Image height
+     * @returns {ImageData} Dithered image data
+     */
     _orderedDither(data, width, height) {
         const newData = new Uint8ClampedArray(data);
         const matrix = [
@@ -211,7 +263,14 @@ const DitherManager = {
         return new ImageData(newData, width, height);
     },
 
-    // Atkinson dithering with block-based processing
+    /**
+     * Applies Atkinson dithering algorithm
+     * Implements error diffusion with block-based processing
+     * @param {Uint8ClampedArray} data - Image data
+     * @param {number} width - Image width
+     * @param {number} height - Image height
+     * @returns {ImageData} Dithered image data
+     */
     _atkinsonDither(data, width, height) {
         const newData = new Uint8ClampedArray(data);
         const threshold = 128;
@@ -255,22 +314,22 @@ const DitherManager = {
 
                     // Distribute error to neighboring blocks (Atkinson pattern)
                     if (x + blockSize < width) {
-                        this._distributeBlockError(newData, x + blockSize, y, c, error / 8, width, blockSize, height); // right
+                        this._distributeBlockError(newData, x + blockSize, y, c, error / 8, width, blockSize, height);
                         if (x + blockSize * 2 < width) {
-                            this._distributeBlockError(newData, x + blockSize * 2, y, c, error / 8, width, blockSize, height); // right + 1
+                            this._distributeBlockError(newData, x + blockSize * 2, y, c, error / 8, width, blockSize, height);
                         }
                     }
                     if (y + blockSize < height) {
                         if (x - blockSize >= 0) {
-                            this._distributeBlockError(newData, x - blockSize, y + blockSize, c, error / 8, width, blockSize, height); // bottom left
+                            this._distributeBlockError(newData, x - blockSize, y + blockSize, c, error / 8, width, blockSize, height);
                         }
-                        this._distributeBlockError(newData, x, y + blockSize, c, error / 8, width, blockSize, height); // bottom
+                        this._distributeBlockError(newData, x, y + blockSize, c, error / 8, width, blockSize, height);
                         if (x + blockSize < width) {
-                            this._distributeBlockError(newData, x + blockSize, y + blockSize, c, error / 8, width, blockSize, height); // bottom right
+                            this._distributeBlockError(newData, x + blockSize, y + blockSize, c, error / 8, width, blockSize, height);
                         }
                     }
                     if (y + blockSize * 2 < height && x + blockSize < width) {
-                        this._distributeBlockError(newData, x + blockSize, y + blockSize * 2, c, error / 8, width, blockSize, height); // bottom + 1
+                        this._distributeBlockError(newData, x + blockSize, y + blockSize * 2, c, error / 8, width, blockSize, height);
                     }
                 }
             }
@@ -279,12 +338,19 @@ const DitherManager = {
         return new ImageData(newData, width, height);
     },
 
-    // Add this as a method in DitherManager
+    /**
+     * Applies Bayer dithering algorithm
+     * Implements threshold-based dithering with block processing
+     * @param {Uint8ClampedArray} data - Image data
+     * @param {number} width - Image width
+     * @param {number} height - Image height
+     * @returns {ImageData} Dithered image data
+     */
     _bayerDither(data, width, height) {
         const newData = new Uint8ClampedArray(data);
         const blockSize = this.currentBlockSize;
         
-        // 4x4 Bayer matrix (simpler and more visible pattern than 8x8)
+        // 4x4 Bayer matrix for pattern generation
         const bayerMatrix = [
             [ 0, 8, 2, 10],
             [12, 4, 14, 6 ],
@@ -292,8 +358,7 @@ const DitherManager = {
             [15, 7, 13, 5 ]
         ];
         
-        // Scale factor to make the pattern more pronounced
-        const scaleFactor = 16; // Increase this value to make pattern more visible
+        const scaleFactor = 16;
         
         // Process in blocks
         for (let y = 0; y < height; y += blockSize) {
@@ -322,7 +387,6 @@ const DitherManager = {
                 
                 // Apply dithering to the block
                 for (let c = 0; c < 3; c++) {
-                    // Normalize pixel value and apply threshold
                     const normalizedPixel = blockAvg[c];
                     const newPixel = normalizedPixel > threshold ? 255 : 0;
                     
@@ -346,7 +410,7 @@ const DitherManager = {
     }
 };
 
-// Update the Bayer dithering to use blocks
+// Legacy functions for backward compatibility
 function bayerDither(imageData, width) {
     const data = new Uint8ClampedArray(imageData.data);
     const height = imageData.data.length / 4 / width;
@@ -424,13 +488,12 @@ function floydSteinbergDither(imageData, width, blockSize) {
 
     const newData = new Uint8ClampedArray(data);
     const threshold = 128;
-    const levels = 4; // Number of quantization levels
+    const levels = 4;
 
     for (let y = 0; y < height; y++) {
         for (let x = 0; x < width; x++) {
             const idx = (y * width + x) * 4;
             
-            // Process each color channel
             for (let c = 0; c < 3; c++) {
                 const oldPixel = data[idx + c];
                 const quantizedPixel = DitherManager._quantize(oldPixel, levels);
@@ -439,17 +502,16 @@ function floydSteinbergDither(imageData, width, blockSize) {
                 
                 newData[idx + c] = newPixel;
                 
-                // Distribute error to neighboring pixels
                 if (x + 1 < width) {
-                    newData[idx + 4 + c] += error * 7 / 16; // right
+                    newData[idx + 4 + c] += error * 7 / 16;
                 }
                 if (y + 1 === height) continue;
                 if (x > 0) {
-                    newData[idx + width * 4 - 4 + c] += error * 3 / 16; // bottom left
+                    newData[idx + width * 4 - 4 + c] += error * 3 / 16;
                 }
-                newData[idx + width * 4 + c] += error * 5 / 16; // bottom
+                newData[idx + width * 4 + c] += error * 5 / 16;
                 if (x + 1 < width) {
-                    newData[idx + width * 4 + 4 + c] += error * 1 / 16; // bottom right
+                    newData[idx + width * 4 + 4 + c] += error * 1 / 16;
                 }
             }
         }
@@ -464,13 +526,12 @@ function atkinsonDither(imageData, width, blockSize) {
 
     const newData = new Uint8ClampedArray(data);
     const threshold = 128;
-    const levels = 4; // Number of quantization levels
+    const levels = 4;
 
     for (let y = 0; y < height; y++) {
         for (let x = 0; x < width; x++) {
             const idx = (y * width + x) * 4;
             
-            // Process each color channel
             for (let c = 0; c < 3; c++) {
                 const oldPixel = data[idx + c];
                 const quantizedPixel = DitherManager._quantize(oldPixel, levels);
@@ -479,24 +540,23 @@ function atkinsonDither(imageData, width, blockSize) {
                 
                 newData[idx + c] = newPixel;
                 
-                // Distribute error to neighboring pixels (Atkinson's algorithm)
                 if (x + 1 < width) {
-                    newData[idx + 4 + c] += error / 8; // right
+                    newData[idx + 4 + c] += error / 8;
                 }
                 if (x + 2 < width) {
-                    newData[idx + 8 + c] += error / 8; // right + 1
+                    newData[idx + 8 + c] += error / 8;
                 }
                 if (y + 1 === height) continue;
                 if (x > 0) {
-                    newData[idx + width * 4 - 4 + c] += error / 8; // bottom left
+                    newData[idx + width * 4 - 4 + c] += error / 8;
                 }
-                newData[idx + width * 4 + c] += error / 8; // bottom
+                newData[idx + width * 4 + c] += error / 8;
                 if (x + 1 < width) {
-                    newData[idx + width * 4 + 4 + c] += error / 8; // bottom right
+                    newData[idx + width * 4 + 4 + c] += error / 8;
                 }
                 if (y + 2 === height) continue;
                 if (x + 1 < width) {
-                    newData[idx + width * 8 + 4 + c] += error / 8; // bottom + 1 right
+                    newData[idx + width * 8 + 4 + c] += error / 8;
                 }
             }
         }
@@ -518,7 +578,7 @@ function orderedDither(imageData, width, blockSize) {
     ];
     const matrixSize = 4;
     const threshold = 128;
-    const levels = 4; // Number of quantization levels
+    const levels = 4;
 
     for (let y = 0; y < height; y++) {
         for (let x = 0; x < width; x++) {
@@ -527,7 +587,6 @@ function orderedDither(imageData, width, blockSize) {
             const matrixY = y % matrixSize;
             const matrixValue = matrix[matrixY][matrixX] * 16;
 
-            // Process each color channel
             for (let c = 0; c < 3; c++) {
                 const pixel = data[idx + c];
                 const quantizedPixel = DitherManager._quantize(pixel, levels);
diff --git a/js/leibovitz/leibovitz.js b/js/leibovitz/leibovitz.js
index 0b5bfdc..769216c 100644
--- a/js/leibovitz/leibovitz.js
+++ b/js/leibovitz/leibovitz.js
@@ -1,3 +1,10 @@
+/**
+ * Main application entry point for the Leibovitz camera app.
+ * Implements a functional architecture with separate managers for each effect.
+ * Uses the Observer pattern for state management and effect application.
+ * Implements the State pattern for mode management (camera/edit).
+ */
+
 const canvas = document.getElementById('canvas');
 const ctx = canvas.getContext('2d');
 const video = document.createElement('video');
@@ -15,16 +22,19 @@ let cameraOn = false;
 let stream = null;
 let track = null;
 let isEditMode = false;
-let originalImage = null; // Store the original image
+let originalImage = null; // Store the original image for edit mode
 
-// Initialize managers
+// Initialize managers - each implements the Observer pattern for state changes
 ColorManager.init();
 DitherManager.init();
 ContrastManager.init();
 BlurManager.init();
 BalanceManager.init();
 
-// Function to update slider controls visibility
+/**
+ * Updates visibility of slider controls based on camera/edit mode state
+ * Uses the State pattern to manage UI visibility
+ */
 function updateSliderControlsVisibility() {
     if (cameraOn || isEditMode) {
         slideControls.classList.add('visible');
@@ -33,36 +43,33 @@ function updateSliderControlsVisibility() {
     }
 }
 
-// Set the canvas dimensions to match the window size
+/**
+ * Updates canvas dimensions while maintaining aspect ratio
+ * Implements the Strategy pattern for different aspect ratio calculations
+ */
 function updateCanvasSize() {
-    // Get the container dimensions
     const container = document.querySelector('.preview-container');
     const containerWidth = container.clientWidth;
     const containerHeight = container.clientHeight;
     
-    // If video is playing, use its aspect ratio
     if (video.videoWidth && video.videoHeight) {
         const videoAspect = video.videoWidth / video.videoHeight;
         const containerAspect = containerWidth / containerHeight;
         
-        // Determine dimensions that maintain aspect ratio while fitting in container
         if (containerAspect > videoAspect) {
-            // Container is wider than video
             canvas.height = containerHeight;
             canvas.width = containerHeight * videoAspect;
         } else {
-            // Container is taller than video
             canvas.width = containerWidth;
             canvas.height = containerWidth / videoAspect;
         }
     } else {
-        // Default to container dimensions until video starts
         canvas.width = containerWidth;
         canvas.height = containerHeight;
     }
 }
 
-// Update canvas size when window is resized
+// Observer pattern: Listen for window resize events
 window.addEventListener('resize', () => {
     if (cameraOn || isEditMode) {
         updateCanvasSize();
@@ -72,38 +79,34 @@ window.addEventListener('resize', () => {
     }
 });
 
-// Initialize canvas size
 updateCanvasSize();
 
-// Function to completely clear the canvas
+/**
+ * Clears the canvas and resets its state
+ * Implements the Command pattern for canvas operations
+ */
 function clearCanvas() {
-    // Get container dimensions
     const container = document.querySelector('.preview-container');
     const containerWidth = container.clientWidth;
     const containerHeight = container.clientHeight;
     
-    // Set canvas dimensions to match container
     canvas.width = containerWidth;
     canvas.height = containerHeight;
-    
-    // Clear the entire canvas context
     ctx.clearRect(0, 0, containerWidth, containerHeight);
-    
-    // Reset any transformations or other context properties
     ctx.setTransform(1, 0, 0, 1, 0, 0);
-    
-    // Hide the canvas
     canvas.style.display = 'none';
 }
 
+/**
+ * Initializes camera access and sets up video stream
+ * Implements the Factory pattern for media device creation
+ * Uses the State pattern for mode management and UI state
+ */
 function startCamera() {
-    // If we're in edit mode, show confirmation dialog
     if (isEditMode) {
         if (!confirm('Switching to camera mode will discard your current image and edits. Continue?')) {
-            return; // User cancelled
+            return;
         }
-        
-        // Clear edit mode state
         isEditMode = false;
         originalImage = null;
         clearCanvas();
@@ -114,19 +117,19 @@ function startCamera() {
             stream = s;
             video.srcObject = stream;
             video.play();
-            canvas.style.display = 'block'; // Show the canvas
+            canvas.style.display = 'block';
             captureButton.disabled = false;
             captureButton.active = true;
             editImageButton.classList.add('hidden');
-            toggleCameraButton.classList.add('hidden'); // Hide the toggle button
+            toggleCameraButton.classList.add('hidden');
             isEditMode = false;
-            originalImage = null; // Clear the original image
-            updateSliderControlsVisibility(); // Show slider controls
+            originalImage = null;
+            updateSliderControlsVisibility();
 
             track = stream.getVideoTracks()[0];
             const settings = track.getSettings();
 
-            // Check if focus control is supported with this browser and device combo
+            // Feature detection for focus control
             if ('focusDistance' in settings) {
                 focusControl.style.display = 'flex';
                 focusSlider.disabled = false;
@@ -145,7 +148,7 @@ function startCamera() {
                 focusControl.style.display = 'none';
             }
 
-            // Draw the video feed to the canvas
+            // Animation loop using requestAnimationFrame
             video.addEventListener('play', function() {
                 function step() {
                     if (!cameraOn) return;
@@ -162,10 +165,14 @@ function startCamera() {
         })
         .catch(err => {
             console.error('Error accessing camera: ', err);
-            toggleCameraButton.classList.remove('hidden'); // Show the button again if there's an error
+            toggleCameraButton.classList.remove('hidden');
         });
 }
 
+/**
+ * Stops camera stream and resets UI state
+ * Implements the Command pattern for cleanup operations
+ */
 function stopCamera() {
     if (stream) {
         stream.getTracks().forEach(track => track.stop());
@@ -177,20 +184,23 @@ function stopCamera() {
         focusControl.style.display = 'none';
         stream = null;
         editImageButton.classList.remove('hidden');
-        toggleCameraButton.classList.remove('hidden'); // Show the toggle button again
-        updateSliderControlsVisibility(); // Hide slider controls if no image is loaded
+        toggleCameraButton.classList.remove('hidden');
+        updateSliderControlsVisibility();
     }
 }
 
+/**
+ * Loads and displays an image file
+ * Implements the Factory pattern for image creation
+ * Uses aspect ratio preservation strategy for responsive display
+ */
 function loadImage(file) {
     const reader = new FileReader();
     reader.onload = function(e) {
         const img = new Image();
         img.onload = function() {
-            // Clear any existing content first
             clearCanvas();
             
-            // Calculate dimensions to maintain aspect ratio
             const container = document.querySelector('.preview-container');
             const containerWidth = container.clientWidth;
             const containerHeight = container.clientHeight;
@@ -208,21 +218,16 @@ function loadImage(file) {
                 canvasHeight = containerWidth / imgAspect;
             }
             
-            // Set canvas dimensions
             canvas.width = canvasWidth;
             canvas.height = canvasHeight;
-            
-            // Store the original image
             originalImage = img;
             
-            // Draw the new image
             ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight);
             canvas.style.display = 'block';
             captureButton.disabled = false;
             captureButton.active = true;
-            updateSliderControlsVisibility(); // Show slider controls
+            updateSliderControlsVisibility();
             
-            // Start the effect loop
             function step() {
                 if (!isEditMode) return;
                 applyEffects();
@@ -235,14 +240,13 @@ function loadImage(file) {
     reader.readAsDataURL(file);
 }
 
+/**
+ * Applies all effects in sequence using the Chain of Responsibility pattern
+ * Each effect is applied using the Strategy pattern for algorithm selection
+ */
 function applyEffects() {
-    // Clear the canvas first
     ctx.clearRect(0, 0, canvas.width, canvas.height);
-    
-    // Draw the original image
     ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height);
-    
-    // Apply effects in sequence
     applyContrast();
     applyColorTint();
     applyBlur();
@@ -250,8 +254,11 @@ function applyEffects() {
     applyDither();
 }
 
+/**
+ * Draws video feed maintaining aspect ratio
+ * Implements the Strategy pattern for aspect ratio handling
+ */
 function drawVideoProportional() {
-    // Clear the canvas first
     ctx.clearRect(0, 0, canvas.width, canvas.height);
     
     const videoAspectRatio = video.videoWidth / video.videoHeight;
@@ -270,10 +277,12 @@ function drawVideoProportional() {
     const offsetX = (canvas.width - drawWidth) / 2;
     const offsetY = (canvas.height - drawHeight) / 2;
     
-    // Draw the video content centered
     ctx.drawImage(video, offsetX, offsetY, drawWidth, drawHeight);
 }
 
+/**
+ * Applies color tint effect using the Strategy pattern
+ */
 function applyColorTint() {
     const currentColor = ColorManager.getCurrentColor();
     if (!currentColor) return;
@@ -283,12 +292,18 @@ function applyColorTint() {
     ctx.putImageData(tintedImageData, 0, 0);
 }
 
+/**
+ * Applies white balance effect using the Strategy pattern
+ */
 function applyBalance() {
     const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
     const balancedImageData = BalanceManager.applyBalance(imageData);
     ctx.putImageData(balancedImageData, 0, 0);
 }
 
+/**
+ * Applies contrast effect using the Strategy pattern
+ */
 function applyContrast() {
     const currentContrast = ContrastManager.getCurrentContrast();
     if (!currentContrast || currentContrast === 1.0) return;
@@ -298,6 +313,9 @@ function applyContrast() {
     ctx.putImageData(contrastedImageData, 0, 0);
 }
 
+/**
+ * Applies dithering effect using the Strategy pattern
+ */
 function applyDither() {
     const currentMode = DitherManager.getCurrentMode();
     if (!currentMode || currentMode === 'none') return;
@@ -307,6 +325,9 @@ function applyDither() {
     ctx.putImageData(ditheredImageData, 0, 0);
 }
 
+/**
+ * Applies blur effect using the Strategy pattern
+ */
 function applyBlur() {
     const currentBlur = BlurManager.getCurrentBlur();
     if (!currentBlur) return;
@@ -316,25 +337,25 @@ function applyBlur() {
     ctx.putImageData(blurredImageData, 0, 0);
 }
 
+/**
+ * Captures the current canvas state with effects
+ * Implements the Command pattern for image capture
+ */
 captureButton.addEventListener('click', () => {
     const currentColor = ColorManager.getCurrentColor();
     const borderWidth = 4;
     
-    // Create a canvas with extra space for the border
     const captureCanvas = document.createElement('canvas');
     const captureCtx = captureCanvas.getContext('2d');
     
-    // Set dimensions including border
     captureCanvas.width = canvas.width + (borderWidth * 2);
     captureCanvas.height = canvas.height + (borderWidth * 2);
     
-    // Fill with border color if a tint is selected
     if (currentColor) {
         captureCtx.fillStyle = currentColor;
         captureCtx.fillRect(0, 0, captureCanvas.width, captureCanvas.height);
     }
     
-    // Draw the main canvas content
     captureCtx.drawImage(canvas, borderWidth, borderWidth);
     
     const link = document.createElement('a');
@@ -343,6 +364,9 @@ captureButton.addEventListener('click', () => {
     link.click();
 });
 
+/**
+ * Toggles camera state using the State pattern
+ */
 toggleCameraButton.addEventListener('click', () => {
     cameraOn = !cameraOn;
     if (cameraOn) {
@@ -354,6 +378,9 @@ toggleCameraButton.addEventListener('click', () => {
     }
 });
 
+/**
+ * Handles image upload using the Factory pattern
+ */
 editImageButton.addEventListener('click', () => {
     if (!cameraOn) {
         imageInput.click();
@@ -367,6 +394,10 @@ imageInput.addEventListener('change', (e) => {
     }
 });
 
+/**
+ * Service Worker registration for offline functionality
+ * Implements the Service Worker pattern for PWA support
+ */
 if ('serviceWorker' in navigator) {
     window.addEventListener('load', () => {
         navigator.serviceWorker.register('/service-worker.js')
@@ -380,14 +411,19 @@ if ('serviceWorker' in navigator) {
 
 ColorManager._setupEventListeners();
 
-// Update the reset handlers in each manager to trigger a redraw
+/**
+ * Resets all effects using the Command pattern
+ */
 function resetEffects() {
     if (isEditMode && originalImage) {
         applyEffects();
     }
 }
 
-// Add reset handlers to each manager
+/**
+ * Reset handlers for each effect manager
+ * Implements the Command pattern for state reset
+ */
 BlurManager.reset = function() {
     this._currentBlur = 0;
     this._slider.value = 0;
@@ -424,6 +460,10 @@ DitherManager.reset = function() {
     resetEffects();
 };
 
+/**
+ * Saves current settings to localStorage
+ * Implements the Memento pattern for state persistence
+ */
 function saveSettings() {
     const settings = {
         blur: BlurManager._currentBlur,
@@ -436,12 +476,15 @@ function saveSettings() {
     localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
 }
 
+/**
+ * Loads saved settings from localStorage
+ * Implements the Memento pattern for state restoration
+ */
 function loadSettings() {
     const savedSettings = localStorage.getItem(SETTINGS_KEY);
     if (savedSettings) {
         const settings = JSON.parse(savedSettings);
         
-        // Apply saved settings
         if (settings.blur !== undefined) {
             BlurManager._currentBlur = settings.blur;
             BlurManager._slider.value = settings.blur;
@@ -478,29 +521,22 @@ function loadSettings() {
     }
 }
 
-// Add event listeners for settings changes
+/**
+ * Sets up event listeners for settings changes
+ * Implements the Observer pattern for state change detection
+ */
 function setupSettingsListeners() {
-    // Blur
     BlurManager._slider.addEventListener('change', saveSettings);
-    
-    // Contrast
     ContrastManager._slider.addEventListener('change', saveSettings);
-    
-    // Color
     ColorManager._colorInput.addEventListener('change', saveSettings);
-    
-    // Balance
     BalanceManager.balanceSlider.addEventListener('change', saveSettings);
-    
-    // Dither
     DitherManager._modeSelect.addEventListener('change', saveSettings);
     
-    // Block Size
     if (DitherManager._blockSizeSlider) {
         DitherManager._blockSizeSlider.addEventListener('change', saveSettings);
     }
 }
 
-// Call this after all managers are initialized
+// Initialize settings
 setupSettingsListeners();
 loadSettings();
\ No newline at end of file