Client-Side Computer Vision & Neural Image Processing: Alpha Matting, ViT & Local Alt-Text Runbook
A production engineering runbook for running computer vision neural networks entirely client-side in the browser. Master neural alpha matting (MODNet), Vision Transformers (ViT) paired with GPT-2 decoders for automated WCAG alt-text, Canvas 2D pixel manipulation, RGBA tensor normalization, and zero-data-leakage visual computing with WebGPU and WebAssembly.
1. In-Browser Computer Vision Architecture: HTML Canvas, Tensors & Zero-Pixel Leakage
// Converts an HTMLImageElement or Canvas to a normalized planar NCHW Tensor
export function imageToNormalizedTensor(
img: HTMLImageElement | HTMLCanvasElement,
targetWidth = 512,
targetHeight = 512
): { tensorData: Float32Array; dims: number[] } {
const canvas = document.createElement('canvas');
canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
if (!ctx) throw new Error('Failed to acquire 2D canvas context');
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
const { data } = ctx.getImageData(0, 0, targetWidth, targetHeight);
// Allocate planar buffer for 3 channels (RGB)
const numPixels = targetWidth * targetHeight;
const tensorData = new Float32Array(3 * numPixels);
// Standard ImageNet normalization coefficients
const mean = [0.485, 0.456, 0.406];
const std = [0.229, 0.224, 0.225];
for (let i = 0; i < numPixels; i++) {
const r = data[i * 4] / 255.0;
const g = data[i * 4 + 1] / 255.0;
const b = data[i * 4 + 2] / 255.0;
// Planar layout: Channel 0 (R), Channel 1 (G), Channel 2 (B)
tensorData[i] = (r - mean[0]) / std[0];
tensorData[numPixels + i] = (g - mean[1]) / std[1];
tensorData[2 * numPixels + i] = (b - mean[2]) / std[2];
}
return { tensorData, dims: [1, 3, targetHeight, targetWidth] };
}Normalizes interleaved 8-bit RGBA canvas pixels into a planar [1, 3, H, W] 32-bit floating-point tensor compatible with ONNX vision models.
| Pipeline Phase | Input Format | Output Format | Typical Latency | Bottleneck Risk | Copy |
|---|---|---|---|---|---|
| Canvas Decode | Blob / File (JPEG/PNG) | OffscreenCanvas ImageData | 10 - 30 ms | Large 4K+ image decode RAM spikes | |
| Tensor Packing | Interleaved Uint8ClampedArray | Planar Float32Array [1, 3, H, W] | 5 - 15 ms | Garbage collection during array allocations | |
| WebGPU Forward Pass | Input Tensor Buffer | Raw Output Logits / Alpha Matte | 25 - 90 ms | VRAM allocation & GPU shader compile | |
| Mask Compositing | Float32 Alpha Probability | 32-bit Transparent PNG Blob | 15 - 40 ms | canvas.toBlob() compression overhead |
2. Neural Alpha Matting with MODNet: Sub-Pixel Foreground Extraction
// Applies an estimated alpha matte to the original full-resolution image
export async function compositeTransparentImage(
sourceImg: HTMLImageElement,
alphaMatteData: Float32Array,
matteWidth: number,
matteHeight: number
): Promise<Blob> {
const width = sourceImg.naturalWidth;
const height = sourceImg.naturalHeight;
// 1. Create matching offscreen canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('Failed to acquire canvas context');
// 2. Render source image
ctx.drawImage(sourceImg, 0, 0, width, height);
const imgData = ctx.getImageData(0, 0, width, height);
const pixels = imgData.data;
// 3. Create intermediate canvas to upscale matte if model ran at fixed 512x512
const matteCanvas = document.createElement('canvas');
matteCanvas.width = matteWidth;
matteCanvas.height = matteHeight;
const matteCtx = matteCanvas.getContext('2d')!;
const matteImgData = matteCtx.createImageData(matteWidth, matteHeight);
for (let i = 0; i < alphaMatteData.length; i++) {
const a = Math.round(Math.min(1, Math.max(0, alphaMatteData[i])) * 255);
matteImgData.data[i * 4 + 3] = a; // alpha channel
}
matteCtx.putImageData(matteImgData, 0, 0);
// 4. Sample upscaled matte onto target image alpha channel
const upCanvas = document.createElement('canvas');
upCanvas.width = width;
upCanvas.height = height;
const upCtx = upCanvas.getContext('2d')!;
upCtx.drawImage(matteCanvas, 0, 0, width, height);
const upMatte = upCtx.getImageData(0, 0, width, height).data;
for (let i = 0; i < width * height; i++) {
pixels[i * 4 + 3] = upMatte[i * 4 + 3];
}
ctx.putImageData(imgData, 0, 0);
// 5. Export lossless transparent PNG
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new Error('Export failed'))), 'image/png');
});
}Composites the continuous neural alpha channel over the original high-resolution image buffer, producing a clean transparent PNG.
| Method | Boundary Quality | Download Size | Trimap Required? | Best Suited For | Copy |
|---|---|---|---|---|---|
| MODNet (Neural Matting) | Sub-pixel feathered alpha (0..255) | ~25 MB (ONNX) | No (Trimap-free) | Portraits, hair, product e-commerce cutouts | |
| RMBG / BirefNet | Ultra-crisp high-detail matting | ~175 MB (ONNX) | No | Complex outdoor subjects and studio photography | |
| DeepLabV3+ (Binary) | Hard binary threshold mask (0 or 1) | ~15 MB (WASM) | No | Coarse object bounding and semantic labeling | |
| Chroma Keying (Color) | Color-distance thresholding | 0 MB (Pure JS) | Green/Blue Screen Only | Controlled studio video and rigid flat backgrounds |
3. Vision Transformers (ViT) & GPT-2: Generating WCAG 2.2 Alt-Text Locally
<img> snippet with width, height, and decoding="async" attributes.import { pipeline } from '@huggingface/transformers';
export async function generateAccessibleAltText(
imageSource: string | HTMLImageElement
): Promise<{ caption: string; htmlSnippet: string }> {
// 1. Initialize vision encoder-decoder pipeline
const captioner = await pipeline(
'image-to-text',
'Xenova/vit-gpt2-image-captioning',
{
device: 'webgpu', // Automatic fallback to WASM handled by runtime
dtype: 'fp32'
}
);
// 2. Generate natural language description
const result = await captioner(imageSource, {
max_new_tokens: 40,
num_beams: 2
});
const rawCaption = result[0]?.generated_text?.trim() || 'Descriptive image';
// Capitalize first letter and ensure ending period
const formattedCaption = rawCaption.charAt(0).toUpperCase() + rawCaption.slice(1);
const cleanAlt = formattedCaption.replace(/\.$/, '') + '.';
// 3. Construct production WCAG-compliant HTML snippet
const htmlSnippet = `<img src="image.webp" alt="${cleanAlt}" loading="lazy" decoding="async" />`;
return { caption: cleanAlt, htmlSnippet };
}Passes an image buffer into ViT-GPT2 to generate an accessible alt-text description without transmitting image pixels over any network.
| Alt-Text Component | WCAG AA Compliance Rule | Bad Anti-Pattern | ViT-Generated Standard | Copy |
|---|---|---|---|---|
| Descriptive Core | Succinctly state the central subject & action | alt="image" or alt="DSC_0041.jpg" | alt="A golden retriever catching a tennis ball in a park." | |
| Length Budget | Under 125 characters for screen reader flow | 3-paragraph essay inside alt attribute | Single complete sentence capturing visual essence | |
| Punctuation | End with a period for screen reader pause | Unterminated string runs into adjacent text | Guaranteed terminal period for natural screen reader speech | |
| Redundant Words | Never include "picture of" or "image of" | alt="Image of a blue bicycle against a wall" | Screen readers announce <img> tag automatically; omit preamble |
4. Memory Management: Preventing VRAM Leaks & Garbage Collector Thrashing
<img> element has loaded or when the user resets the workspace.export class VisionResourceManager {
private activeUrls: Set<string> = new Set();
private sharedCanvas: HTMLCanvasElement | null = null;
// Reusable canvas to prevent DOM garbage thrashing
public getCanvas(width: number, height: number): HTMLCanvasElement {
if (!this.sharedCanvas) {
this.sharedCanvas = document.createElement('canvas');
}
this.sharedCanvas.width = width;
this.sharedCanvas.height = height;
return this.sharedCanvas;
}
// Safe object URL generator with tracked lifecycle
public createSafeUrl(blob: Blob): string {
const url = URL.createObjectURL(blob);
this.activeUrls.add(url);
return url;
}
// Revoke single URL
public revokeUrl(url: string) {
if (this.activeUrls.has(url)) {
URL.revokeObjectURL(url);
this.activeUrls.delete(url);
}
}
// Full teardown on route change or component unmount
public dispose() {
for (const url of this.activeUrls) {
URL.revokeObjectURL(url);
}
this.activeUrls.clear();
if (this.sharedCanvas) {
this.sharedCanvas.width = 0;
this.sharedCanvas.height = 0;
this.sharedCanvas = null;
}
}
}Resource lifecycle controller preventing memory leaks by tracking and revoking object URLs and recycling canvas buffers.
| Resource Type | Leak Mechanism | Symptoms in Browser | Preventative Architecture | Copy |
|---|---|---|---|---|
| Blob Object URLs | Unrevoked URL.createObjectURL() references | Continuous browser RAM growth | Always invoke URL.revokeObjectURL() upon image render | |
| Canvas Buffers | Allocating new canvas per inference | Frequent GC pauses (100-300ms stutter) | Maintain a singleton reusable OffscreenCanvas instance | |
| WebGPU Buffers | Failure to call buffer.destroy() | GPU process VRAM exhaustion / crash | Explicit destroy() calls on intermediate tensor passes | |
| ImageData Readbacks | getImageData() without willReadFrequently | Costly GPU bus synchronization stalls | Pass { willReadFrequently: true } to getContext("2d") |
5. In-Browser Computer Vision Models & Specifications Cheat Sheet
// Verify if the host GPU supports modern 16-bit float tensor operations
export async function checkGpuVisionFeatures(): Promise<{
supportsWebGpu: boolean;
supportsFp16: boolean;
maxTextureDimension2D: number;
}> {
if (!('gpu' in navigator)) {
return { supportsWebGpu: false, supportsFp16: false, maxTextureDimension2D: 0 };
}
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
return { supportsWebGpu: false, supportsFp16: false, maxTextureDimension2D: 0 };
}
return {
supportsWebGpu: true,
supportsFp16: adapter.features.has('shader-f16'),
maxTextureDimension2D: adapter.limits.maxTextureDimension2D
};
}Inspects WebGPU adapter limits and checks for shader-f16 support to enable faster, half-precision vision inference.
| Model & Architecture | Task Domain | Weights Download | Inference Latency | Recommended Acceleration | Copy |
|---|---|---|---|---|---|
| MODNet (CNN) | Alpha Matting / Cutout | ~25 MB | 40 - 120 ms | WebGPU (60 FPS capable) or WASM SIMD | |
| ViT-GPT2 (Transformer) | Image Captioning & Alt-Text | ~110 MB | 350 - 900 ms | WebGPU (recommended for cross-attention) | |
| CLIP-ViT-B/32 | Zero-Shot Classification / Embeddings | ~150 MB | 150 - 300 ms | WebGPU / WASM SIMD multi-threaded | |
| YOLOv8-Nano (CNN) | Object Detection & Bounding Boxes | ~12 MB | 25 - 60 ms | WebGPU or CPU WASM for real-time video |
Related Free Online Tools
Put these techniques into practice immediately with our privacy-first browser utilities:
Extract transparent foregrounds 100% locally in your browser using neural alpha matting.
Generate descriptive image captions and WCAG-compliant alt-text attributes locally.
Encode and decode binary image tensors, data URLs, and canvas buffers.