Generated banner for Dither Background
Generated logo for Dither Background

Dither Background

Meng ToCommunityCreated by Meng To

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Medium risk

From the author

Create a dark monochrome procedural background with enlarged square pixels and visible Bayer-style ordered dithering. Use when a page needs an atmospheric near-black dither field, broad organic waves or cloud masses, and restrained gray-white highlights behind framed UI, hero content, or data overlays.
— the description by Meng To

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a JavaScript-based recipe for generating procedural, dithered backgrounds using HTML5 Canvas. It creates organic, cloud-like masses and waves using a 4x4 Bayer-style ordered dithering pattern. Designed for dark-mode interfaces, it renders a near-black base with charcoal and gray highlights, intended to sit behind UI elements, hero content, or data overlays without distracting from foreground information.

What it does

  • Generates a procedural monochrome background using a 4x4 Bayer dither matrix.
  • Creates organic wave and cloud-like shapes using fractal Brownian motion.
  • Implements a vignetted effect to focus visual interest and ensure edge readability.
  • Provides a configurable JavaScript function to control cell size, color palette, and motion.
  • Includes responsive resizing logic to maintain visual consistency across viewports.
  • Supports reduced-motion preferences to minimize background animation.

When to use it

  • When you need an atmospheric, dark-themed background layer for a web interface.
  • When the design requires a specific retro-inspired aesthetic using visible square pixels.
  • When you need to add procedural depth behind framed UI or data-heavy overlays.
  • When you want to avoid colorful gradients in favor of a restrained, monochrome palette.

Advantages

  • Maintains a consistent, non-distracting visual style for dark interfaces.
  • Provides clear control over the dither pattern and pixel cell size.
  • Includes built-in support for accessibility via reduced-motion media queries.
  • Offers a lightweight procedural approach compared to large image assets.

Usage examples

“Create a dark atmospheric background for my dashboard using the dither-background skill.”
The agent provides the HTML canvas element and the JavaScript initialization code to render the procedural dithered background behind your dashboard UI.
“How can I adjust the dither background to have larger pixels?”
The agent explains how to modify the cellSize parameter in the initDitherBackground function to increase the size of the square pixels.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/mengto/dither-background/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip dither-background.zip -d .agents/skills

Documentation by Meng To

Dither Background

Use When

  • A dark interface needs an atmospheric monochrome background layer.

  • The visual should show enlarged square pixels and visible ordered dithering.

  • The design calls for organic waves, cloud-like masses, or procedural depth without colorful gradients.

  • The background should support framed UI, hero content, or data overlays.

Visual Target

  • Near-black base with charcoal midtones, soft gray buildup, and occasional white highlights.

  • Clearly visible square pixel cells, not tiny film grain.

  • 4x4 Bayer-style dither pattern or equivalent ordered thresholding.

  • Broad organic waves or cloud-like masses, not random TV noise.

  • Vignetted edges so the brighter mass sits centrally or off-axis.

HTML And CSS

<canvas class="dither-background" data-dither-background></canvas>
.dither-background {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100vw;
  height: 100vh;
  background: #030303;
  pointer-events: none;
}

.page-content {
  position: relative;
  z-index: 1;
}

Canvas Recipe

Use a real canvas when motion or procedural depth is needed.

const BAYER_4X4 = [
   0,  8,  2, 10,
  12,  4, 14,  6,
   3, 11,  1,  9,
  15,  7, 13,  5,
].map((value) => (value + 0.5) / 16);

function smoothstep(edge0, edge1, value) {
  const t = Math.max(0, Math.min(1, (value - edge0) / (edge1 - edge0)));
  return t * t * (3 - 2 * t);
}

function noise2(x, y) {
  const value = Math.sin(x * 127.1 + y * 311.7) * 43758.5453123;
  return value - Math.floor(value);
}

function valueNoise(x, y) {
  const ix = Math.floor(x);
  const iy = Math.floor(y);
  const fx = x - ix;
  const fy = y - iy;
  const ux = fx * fx * (3 - 2 * fx);
  const uy = fy * fy * (3 - 2 * fy);

  const a = noise2(ix, iy);
  const b = noise2(ix + 1, iy);
  const c = noise2(ix, iy + 1);
  const d = noise2(ix + 1, iy + 1);
  return (
    a * (1 - ux) * (1 - uy) +
    b * ux * (1 - uy) +
    c * (1 - ux) * uy +
    d * ux * uy
  );
}

function fbm(x, y) {
  let value = 0;
  let amplitude = 0.5;
  let frequency = 1;

  for (let octave = 0; octave < 4; octave++) {
    value += valueNoise(x * frequency, y * frequency) * amplitude;
    frequency *= 2.02;
    amplitude *= 0.5;
  }

  return value;
}

function initDitherBackground(canvas, options = {}) {
  if (!canvas) return () => {};

  const ctx = canvas.getContext("2d", { alpha: false });
  if (!ctx) return () => {};

  const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  const cell = options.cellSize || 7;
  const maxDpr = options.maxDpr || 1.5;
  let width = 1;
  let height = 1;
  let cols = 1;
  let rows = 1;
  let rafId = 0;

  const palette = options.palette || [
    [3, 3, 3],
    [16, 16, 17],
    [34, 35, 37],
    [74, 75, 78],
    [168, 169, 171],
    [236, 236, 232],
  ];

  function resize() {
    const dpr = Math.min(window.devicePixelRatio || 1, maxDpr);
    width = Math.max(1, window.innerWidth);
    height = Math.max(1, window.innerHeight);
    canvas.width = Math.floor(width * dpr);
    canvas.height = Math.floor(height * dpr);
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    cols = Math.ceil(width / cell);
    rows = Math.ceil(height / cell);
  }

  function sampleField(x, y, time) {
    const nx = (x / cols - 0.5) * 2;
    const ny = (y / rows - 0.5) * 2;
    const distance = Math.sqrt(nx * nx * 0.84 + ny * ny * 1.28);
    const vignette = 1 - smoothstep(0.18, 1.15, distance);
    const drift = reduceMotion ? 0 : time * 0.018;

    const wave =
      Math.sin(nx * 2.8 + ny * 1.2 + drift) * 0.18 +
      Math.sin(nx * -1.4 + ny * 3.8 - drift * 0.8) * 0.14;
    const cloud = fbm(nx * 1.35 + drift * 0.16, ny * 1.35 - drift * 0.08);
    const ridge = smoothstep(0.48, 0.92, cloud + wave);
    const offAxisMass = smoothstep(0.98, 0.18, Math.hypot(nx + 0.22, ny - 0.08));

    return Math.max(0, Math.min(1, ridge * vignette * 0.92 + offAxisMass * 0.18));
  }

  function render(time = 0) {
    const seconds = time * 0.001;
    ctx.fillStyle = "rgb(3,3,3)";
    ctx.fillRect(0, 0, width, height);

    for (let y = 0; y < rows; y++) {
      for (let x = 0; x < cols; x++) {
        const threshold = BAYER_4X4[(y % 4) * 4 + (x % 4)];
        const brightness = sampleField(x, y, seconds);
        const stepped = Math.floor(Math.max(0, Math.min(0.999, brightness + threshold * 0.18)) * palette.length);
        const color = palette[Math.min(palette.length - 1, stepped)];
        ctx.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`;
        ctx.fillRect(x * cell, y * cell, cell, cell);
      }
    }

    if (!reduceMotion) rafId = requestAnimationFrame(render);
  }

  function handleResize() {
    cancelAnimationFrame(rafId);
    resize();
    render();
  }

  resize();
  render();
  window.addEventListener("resize", handleResize);

  return () => {
    cancelAnimationFrame(rafId);
    window.removeEventListener("resize", handleResize);
  };
}

const cleanupDither = initDitherBackground(
  document.querySelector("[data-dither-background]"),
  {
    cellSize: 7,
    maxDpr: 1.5,
  }
);

Tuning Knobs

  • Cell size: 5px-10px; larger cells make the Bayer matrix more legible.

  • Palette: near-black, charcoal, soft gray, rare white highlights only.

  • Shape: tune wave, cloud, ridge, and offAxisMass to create broad masses.

  • Vignette: increase edge falloff when foreground readability needs more quiet.

  • Motion: keep drift slow; use low time multipliers and avoid flicker.

  • Performance: increase cellSize or cap maxDpr before simplifying the field.

Composition Notes

  • Put the canvas behind the interface with pointer-events: none.

  • Use it as atmosphere behind framed UI, hero copy, or data overlays.

  • Keep foreground contrast controlled and typography clean.

  • Let one main bright mass define the composition; avoid even full-screen brightness.

Avoid

  • Rainbow gradients, colorful noise, or soft blurry blobs without dither structure.

  • Tiny grain or static-like speckle where the square matrix disappears.

  • Bright full-screen white noise that competes with foreground type.

  • Random per-frame noise that flickers instead of drifting.

  • Covering the entire viewport with equally bright cells.

Quick Checks

  • The square Bayer pattern is visible from normal viewing distance.

  • The field forms broad organic waves or cloud masses.

  • The palette stays monochrome and restrained.

  • Edges recede into near-black.

  • Foreground UI remains readable without heavy overlays.

Files

LICENSE

MIT License

Copyright (c) 2026 Meng To

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

What our scanner noticed· Medium risk

  • Large base64-encoded blobs

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Meng To
Publisher
Meng To (Community)
Source
MengTo/Skills · agent-skills/web-design/dither-background · commit 5ea1661
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Meng To.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download dither-background.zip