GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

Voronoi Diagram Generator — 9 Patterns

A voronoi diagram isn't lines drawn by hand — it's every point finding its nearest seed value.

Auto-plays · click a tile to jump to its section · all nine in one zip

These nine aren't nine unrelated demos — they're one nearest-seed calculation wearing nine different outputs. Items 1 through 3 keep the seeds still and only change what happens to the same edge gap (a flat mosaic, an inked crack, a swept glass panel). Item 4 swaps the cell shape itself for the dual triangle mesh. Item 5 adds a moving seed under the pointer, item 6 strips the fill down to bare edges, item 7 trades per-frame recomputation for a precomputed relaxation sequence, item 8 warps the sample space with noise before anything is measured, and item 9 stacks three of those computations into one hero scene.

What Turns a Random Scatter Into Nine Voronoi Cell Patterns

Every pattern below runs its math on a hidden 160×100 canvas, not the full display size — nearestTwo() walks every seed for a given pixel, keeps the squared distance to the closest one and the runner-up, and only that small buffer gets scaled up to fill the visible box (imageSmoothingEnabled = false, so the upscale stays crisp rather than blurred). Nothing here reads a clock, a click count, or a real dataset: each item seeds its own tiny random-number generator with a fixed integer (409011 through 409091), so the same mosaic, the same cracks, and the same triangle mesh render identically on every reload. For the pixel buffer these nine write into, MDN's Canvas API overview covers the 2D context these renderField() functions all draw through.

01Mosaic Cells Filled by the Seed-count Value

Each seed coordinate value claims its nearest region as a polygon cell, and every cell fills with its own color to form a background mosaic. That fill comes from nearestTwo(), which checks every seed's squared distance to a pixel and keeps the closest one (ND) and the runner-up (ND2); wherever the gap edge = ND2 - ND drops under 1.5, the cell darkens toward its neighbor's border.

Mosaic Cells Filled by the Seed-count Value
Open in a new tab

01 · Mosaic Cells Filled by the Seed-count Value — Measured area 60.149% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

VORONOI_SEED_COUNTdriftSeedsmosaicPalette
  function renderField(t) {
    driftSeeds(t);
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        nearestTwo(x + 0.5, y + 0.5, seeds);
        var rgb = paletteRgb[seeds[NEI].c];
        var edge = ND2 - ND;
        var dark = edge < 1.5 ? 0.5 + edge / 3 : 1;
        offBuf[idx] = rgb[0] * dark;
        offBuf[idx + 1] = rgb[1] * dark;
        offBuf[idx + 2] = rgb[2] * dark;
        offBuf[idx + 3] = 255;
        idx += 4;
      }
    }
  }

Across the captured frames this canvas repaints a 60.1% cumulative area at an 84.0 average intensity, moving on 23/23 tracked frames.

02Cells Whose Boundary-width Value Cracks Like Dry Earth

Combining a boundary thickness value with a brightness value along each edge produces a texture that reads like dry, cracked ground. The same nearestTwo() gap decides the crack here: once edge falls under CRACK_WIDTH_VALUE, the pixel blends toward an ink color by crackPulsePhase, a sine wave that makes the cracks visibly darken and lighten instead of sitting flat.

Cells Whose Boundary-width Value Cracks Like Dry Earth
Open in a new tab

02 · Cells Whose Boundary-width Value Cracks Like Dry Earth — Measured area 73.787% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

CRACK_WIDTH_VALUEcrackPulsePhasedriftSeeds
  function renderField(t) {
    driftSeeds(t);
    var crackPulsePhase = 0.5 + 0.5 * Math.sin(t * PULSE_FREQ);
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        nearestTwo(x + 0.5, y + 0.5, seeds);
        var rgb = paletteRgb[seeds[NEI].c];
        var band = 0.85 + 0.15 * Math.sin(ND * 0.4 + NEI);
        var edge = ND2 - ND;
        var r, g, b;
        if (edge < CRACK_WIDTH_VALUE) {
          var m = 0.15 + 0.55 * crackPulsePhase;
          r = rgb[0] + (INK_RGB[0] - rgb[0]) * m;
          g = rgb[1] + (INK_RGB[1] - rgb[1]) * m;
          b = rgb[2] + (INK_RGB[2] - rgb[2]) * m;
        } else {
          r = rgb[0] * band; g = rgb[1] * band; b = rgb[2] * band;
        }
        offBuf[idx] = r; offBuf[idx + 1] = g; offBuf[idx + 2] = b; offBuf[idx + 3] = 255;
        idx += 4;
      }
    }
  }

Its measured pass shows a 73.8% cumulative area at 47.9 intensity, again moving on 23/23 frames.

03Stained-glass Panel Where Each Cell Carries Its Own Light-transmission Value

Each cell's color value and border-thickness value are arranged like glass panes, producing the look of light passing through stained glass. Because these seeds never move, nearestTwo() only runs once into two cached arrays (cellIdx, edgeVal); every frame after that just projects a pixel onto a sweep angle and adds a Gaussian glow band, so the panel's only per-frame cost is a light sweep, not a re-solved diagram.

Stained-glass Panel Where Each Cell Carries Its Own Light-transmission Value
Open in a new tab

03 · Stained-glass Panel Where Each Cell Carries Its Own Light-transmission Value — Measured area 72.633% · moving frames 20/23Original by GODRICH · SCSS and React ship in the zip · See the password

GLASS_BORDER_PXlightSweepAnglestainedPalette
  (function precompute() {
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        nearestTwo(x + 0.5, y + 0.5, seeds);
        cellIdx[idx] = NEI;
        edgeVal[idx] = ND2 - ND;
        idx += 1;
      }
    }
  })();
  var lightSweepAngle = 0.62;
  var cosA = Math.cos(lightSweepAngle), sinA = Math.sin(lightSweepAngle);
  var projSpan = Math.abs(CALC_W * cosA) + Math.abs(CALC_H * sinA);

The panel's measured pass reads 72.6% cumulative area at 42.4 intensity, moving on 20/23 frames — the lowest frame count of the nine, since the cell layout itself never changes, only the light band crossing it.

04Delaunay Triangle Mesh That Morphs by a Vertex-jitter Value

The vertex-jitter value of a triangle mesh built from the same seed coordinates changes over time, so the surface slowly ripples. Rather than a nearest-seed lookup, item 4 rebuilds a full Delaunay triangulation from scratch every frame — circumcircle() solves the circle through three points, and delaunayTriangulate() throws out and rebuilds any triangle whose circumcircle contains the next jittered point.

Delaunay Triangle Mesh That Morphs by a Vertex-jitter Value
Open in a new tab

04 · Delaunay Triangle Mesh That Morphs by a Vertex-jitter Value — Measured area 66.146% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

VERTEX_JITTER_PXdelaunayTriangulatecircumcircle
  function circumcircle(a, b, c) {
    var ax = a[0], ay = a[1], bx = b[0], by = b[1], cx = c[0], cy = c[1];
    var d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
    if (Math.abs(d) < 1e-6) return null;
    var ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
    var uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
    var r2 = (ux - ax) * (ux - ax) + (uy - ay) * (uy - ay);
    return { x: ux, y: uy, r2: r2 };
  }

Its measured pass shows a 66.1% cumulative area at 75.1 intensity, moving on 23/23 frames — this is the only one of the nine that fills and strokes vector triangles directly on the canvas, the dual graph of a voronoi diagram, instead of computing a per-pixel raster fill.

05Cells Whose Boundary the Pointer Pushes as an Extra Seed

The pointer position value is added as a temporary seed, so the nearer the cursor gets, the further the surrounding cell-boundary value is pushed away. repelSeeds() runs before the nearest-seed scan: it measures each seed's distance to the pointer (or a virtual orbiting point when idle), and any seed inside SEED_REPEL_RADIUS_PX gets shoved outward by a push-squared falloff before the diagram is drawn.

Cells Whose Boundary the Pointer Pushes as an Extra Seed
Open in a new tab

05 · Cells Whose Boundary the Pointer Pushes as an Extra Seed — Measured area 54.09% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

SEED_REPEL_RADIUS_PXrepelSeedsvirtualPointerOrbit
  function repelSeeds(pointerCalc) {
    var radiusCalc = SEED_REPEL_RADIUS_PX * (CALC_W / cw);
    seeds.forEach(function (s) {
      var dx = s.bx - pointerCalc.x, dy = s.by - pointerCalc.y;
      var dist = Math.sqrt(dx * dx + dy * dy) || 0.0001;
      if (dist < radiusCalc) {
        var push = (radiusCalc - dist) / radiusCalc;
        var k = push * push * 0.95;
        s.x = s.bx + (dx / dist) * k * radiusCalc;
        s.y = s.by + (dy / dist) * k * radiusCalc;
      } else {
        s.x = s.bx; s.y = s.by;
      }
    });
  }

Its measured pass is the lightest of the nine — 54.1% cumulative area at 103.4 intensity, the highest intensity score here, on 23/23 frames.

06Cells Reduced to Glowing Network Edges

The cell-fill value is dropped and only the boundary brightness value remains, so the diagram glows like a technical network diagram. It reuses the same edge = ND2 - ND gap as item 1, but instead of tinting a filled cell it blends straight from the background color to the cell color, so only the boundary band is ever visibly painted.

Cells Reduced to Glowing Network Edges
Open in a new tab

06 · Cells Reduced to Glowing Network Edges — Measured area 63.17% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

EDGE_GLOW_WIDTHdriftSeedsglowPalette
  function renderField(t) {
    driftSeeds(t);
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        nearestTwo(x + 0.5, y + 0.5, seeds);
        var edge = ND2 - ND;
        var r, g, b;
        if (edge < EDGE_GLOW_WIDTH) {
          var glowT = 1 - edge / EDGE_GLOW_WIDTH;
          var rgb = paletteRgb[seeds[NEI].c];
          r = BG_RGB[0] + (rgb[0] - BG_RGB[0]) * glowT;
          g = BG_RGB[1] + (rgb[1] - BG_RGB[1]) * glowT;
          b = BG_RGB[2] + (rgb[2] - BG_RGB[2]) * glowT;
        } else {
          r = BG_RGB[0]; g = BG_RGB[1]; b = BG_RGB[2];
        }
        offBuf[idx] = r; offBuf[idx + 1] = g; offBuf[idx + 2] = b; offBuf[idx + 3] = 255;
        idx += 4;
      }
    }
  }

Its measured pass shows a 63.2% cumulative area at 84.7 intensity, moving on 23/23 frames.

07Cells That Even Out as the Iteration-count Value Grows

Each time the relaxation iteration-count value grows, the seed coordinate values move toward their cell centers, and cell sizes gradually even out. computeCentroids() sums every pixel's coordinates by which seed claims it, then averages those sums into new seed positions — repeating that step LLOYD_RELAXATION_STEPS times up front produces a chain of layouts that renderField() blends between as playback time advances.

Cells That Even Out as the Iteration-count Value Grows
Open in a new tab

07 · Cells That Even Out as the Iteration-count Value Grows — Measured area 36.754% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

LLOYD_RELAXATION_STEPScomputeCentroidsrelaxationStates
  function computeCentroids(positions) {
    var sumX = new Array(positions.length).fill(0);
    var sumY = new Array(positions.length).fill(0);
    var cnt = new Array(positions.length).fill(0);
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        nearestTwo(x + 0.5, y + 0.5, positions);
        sumX[NEI] += x + 0.5; sumY[NEI] += y + 0.5; cnt[NEI] += 1;
      }
    }
    return positions.map(function (p, i) {
      if (!cnt[i]) return { x: p.x, y: p.y, c: p.c };
      return { x: sumX[i] / cnt[i], y: sumY[i] / cnt[i], c: p.c };
    });
  }

Its measured pass is the calmest of the nine — 36.8% cumulative area at 57.1 intensity, on 23/23 frames, because a nearly-relaxed layout moves less between one saved state and the next.

08Camouflage Pattern Built From a Cell-size Variance Value

Seed density value differs from region to region, so the cell-size variance value grows large, and the result reads as an irregular animal-camouflage pattern. Before the nearest-seed check runs, warpCoord() bends the sample point itself using valueNoise2D(), a bilinear read off a 9×6 random grid, so two pixels that would otherwise land in the same cell can be pushed into different ones.

Camouflage Pattern Built From a Cell-size Variance Value
Open in a new tab

08 · Camouflage Pattern Built From a Cell-size Variance Value — Measured area 27.205% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

NOISE_WARP_PXvalueNoise2DwarpCoord
  function valueNoise2D(gx, gy, arr) {
    var x0 = Math.floor(gx), y0 = Math.floor(gy);
    var x1 = Math.min(NOISE_GRID_W - 1, x0 + 1), y1 = Math.min(NOISE_GRID_H - 1, y0 + 1);
    var tx = gx - x0, ty = gy - y0;
    var v00 = arr[y0 * NOISE_GRID_W + x0], v10 = arr[y0 * NOISE_GRID_W + x1];
    var v01 = arr[y1 * NOISE_GRID_W + x0], v11 = arr[y1 * NOISE_GRID_W + x1];
    var top = v00 + (v10 - v00) * tx, bot = v01 + (v11 - v01) * tx;
    return top + (bot - top) * ty;
  }
  function warpCoord(x, y, breathe) {
    var gx = x / CALC_W * (NOISE_GRID_W - 1), gy = y / CALC_H * (NOISE_GRID_H - 1);
    var n1 = valueNoise2D(gx, gy, noiseGridX), n2 = valueNoise2D(gx, gy, noiseGridY);
    return { x: x + (n1 - 0.5) * 2 * NOISE_WARP_PX * breathe, y: y + (n2 - 0.5) * 2 * NOISE_WARP_PX * breathe };
  }

Its measured pass is the quietest of the nine — a 27.2% cumulative area at 37.7 intensity, on 23/23 frames, since the noise warp only nudges cell edges rather than resizing whole regions.

09Hero Scene Where Several Values React Together

The seed coordinate value, pointer-repel value, and relaxation-step value are layered into one scene, making the whole background a living voronoi hero. renderField() stacks three of the above computations in one frame: relaxNudge() blends each seed between its raw and twice-relaxed position, then applyPointerRepel() nudges whatever seeds are still near the cursor, before nearestTwo() ever runs.

Hero Scene Where Several Values React Together
Open in a new tab

09 · Hero Scene Where Several Values React Together — Measured area 72.469% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

HERO_SEED_COUNTapplyPointerRepelrelaxNudge
  function applyPointerRepel(pointerCalc) {
    var radiusCalc = HERO_REPEL_RADIUS_PX * (CALC_W / cw);
    seeds.forEach(function (s) {
      var dx = s.x - pointerCalc.x, dy = s.y - pointerCalc.y;
      var dist = Math.sqrt(dx * dx + dy * dy) || 0.0001;
      if (dist < radiusCalc) {
        var push = (radiusCalc - dist) / radiusCalc;
        var k = push * push * 0.85;
        s.x += (dx / dist) * k * radiusCalc;
        s.y += (dy / dist) * k * radiusCalc;
      }
    });
  }

Its measured pass shows a 72.5% cumulative area at 89.4 intensity — the highest intensity outside item 5 — on 23/23 frames, from stacking relaxation and repel in the same pixel loop.

Where a Voronoi Diagram Generator Actually Pays Rent

Item 1's mosaic and item 6's edge-only network both work as landing-page background decoration, sitting quietly behind a headline where a flat gradient would otherwise go. Item 2's cracked-earth texture and item 7's relaxation sequence suit a brand intro or a loading-complete transition — something that visibly settles into a finished pattern right as content is ready to show. Item 3's stained-glass sweep and item 8's camouflage warp read as premium card backgrounds or outdoor-and-fashion product panels, where the texture itself is the selling point rather than a container for text.

Item 4's triangle mesh fits a data-visualization background, since a mesh reads as "structure" the way filled cells read as "regions." Items 5 and 9 are the two built for direct interaction — a proximity-driven hero where a cursor visibly reshapes the layout is worth the extra per-frame cost of repelSeeds() or applyPointerRepel() specifically because a visitor's own mouse becomes part of the composition, not just a passive background.

Where Three Constants Actually Redraw a Cell Boundary

VORONOI_SEED_COUNT (item 1, default 24) decides how many polygons exist before anything else runs — this is the one constant that changes the diagram's structure rather than just its motion. SEED_REPEL_RADIUS_PX (item 5, default 120px, converted into the 160×100 calc space by a CALC_W / cw ratio) decides how far the pointer's influence reaches from the cursor. LLOYD_RELAXATION_STEPS (item 7, default 6) decides how many centroid-averaging passes run before playback even starts, which is what makes the relaxed cells look more evenly sized than the raw mosaic in item 1.

Where Nine Voronoi Cells Break — Reduced Motion, Low-end Devices, and Touch Input

Every one of the nine checks prefers-reduced-motion before its first frame, but the resting states this produces aren't identical across items — this is the real trap. Most items freeze at the halfway point of their loop, but item 2 freezes at a quarter of its pulse (a fainter crack than most viewers would see mid-animation), item 7 freezes 97% of the way into its relaxation sequence (nearly the smoothest layout it ever reaches), and item 5's pointer gets pushed to an off-canvas coordinate entirely, so a reduced-motion visitor never sees any repel at all, only the plain scattered seeds. Copying the reduced-motion branch from one item into another without checking its own resting-state math reproduces the wrong still frame.

The nearestTwo() scan alone checks every one of up to 32 seeds against all 16,000 pixels in the 160×100 calc canvas, every frame — item 4's from-scratch Delaunay rebuild and item 9's stacked relaxation-plus-repel pass are the two heaviest of the nine, and a low-end device is where that cost first shows up as dropped frames rather than a smooth loop. Touch input never gets its own branch: pointermove and pointerleave fire for a finger the same as a mouse, but a finger that lifts off the glass behaves like pointerleave immediately, so items 5 and 9 mostly show their idle virtual-orbit pointer on a touchscreen rather than a sustained repel — the y8nwxyua in the zip is what a real screen needs if that gap matters for a specific layout, since nothing here reads a real click position, a real payment amount, or a real user record.

What Nine Voronoi Canvases Actually Moved, Frame by Frame

Item Cumulative area Intensity Moved frames
01 Mosaic Cells Filled by the Seed-count Value 60.1% 84.0 23/23
02 Cells Whose Boundary-width Value Cracks Like Dry Earth 73.8% 47.9 23/23
03 Stained-glass Panel Where Each Cell Carries Its Own Light-transmission Value 72.6% 42.4 20/23
04 Delaunay Triangle Mesh That Morphs by a Vertex-jitter Value 66.1% 75.1 23/23
05 Cells Whose Boundary the Pointer Pushes as an Extra Seed 54.1% 103.4 23/23
06 Cells Reduced to Glowing Network Edges 63.2% 84.7 23/23
07 Cells That Even Out as the Iteration-count Value Grows 36.8% 57.1 23/23
08 Camouflage Pattern Built From a Cell-size Variance Value 27.2% 37.7 23/23
09 Hero Scene Where Several Values React Together 72.5% 89.4 23/23

Item 5's push-squared falloff produces the sharpest per-pixel intensity of the nine at 103.4, even though its cumulative area is smaller than the static fills — a pointer that shoves a handful of seeds hard beats a background that redraws everywhere gently.

Three Variants, One Seed Value Bent Each Time

Variant Value Feel
Adjust the seed-count value 01 VORONOI_SEED_COUNT 24 → 60 Shortening the value should split the canvas into a few large, sparse cells, reading as simple, while lengthening it should split it into many small, dense cells, reading as an intricate mosaic.
Adjust the pointer repel-radius value 05 SEED_REPEL_RADIUS_PX 120 → 240 Shortening the value should push aside only the cells right next to the cursor, while lengthening it should push cells across a much wider area of the screen.
Adjust the Lloyd relaxation step-count value 07 LLOYD_RELAXATION_STEPS 6 → 20 Shortening the value should leave cell sizes uneven and rough, while lengthening it should converge cell sizes toward an even, smooth arrangement.

Accessibility: What prefers-reduced-motion Actually Removes From These Nine Cells

Once prefers-reduced-motion: reduce is set, playing becomes false and every canvas stops calling requestAnimationFrame after its first still frame — the is-demo class is also removed from the root element, which drops a hover cursor style in the accompanying stylesheet. Nobody who asked for less motion ever sees the drift, the crack pulse, the light sweep, or item 9's blended relaxation happen — they land directly on whichever fixed elapsed value that item's code assigns, which is why the resting frames differ item to item rather than all landing on a single shared paused look.

How a Voronoi Diagram Algorithm Actually Shows Up in Nearest-seed Math

Strip away the palette and the drift, and a voronoi diagram algorithm is just this: for every point in a plane, find the closest of a fixed set of seeds, and color that point by which seed won. nearestTwo() is the one function every non-triangulated item in this set calls, and it's a brute-force voronoi diagram javascript implementation with no external math library — a nested loop over seeds inside a nested loop over pixels, keeping only the smallest and second-smallest squared distance it ever sees. Each of the nine is a small voronoi diagram example built around exactly one value bent away from that same core loop: a threshold, a radius, an iteration count, or a warp added before the loop runs.

Where Else Can You Find voronoi diagram maker Examples Like These?

CSS Background Pattern Generator: 9 Seeded Scenes covers the wider family of seeded-canvas backgrounds this set belongs to, useful if a flat generative pattern is enough and a full cell diagram is more than the layout needs. 9 CSS Loading Animation Patterns, Canvas Physics swaps the nearest-seed rule for real spring and gravity math, worth comparing against item 5's push-squared repel here to see two different ways a pointer or a value can distort a canvas. Conway Game of Life in Canvas, 9 Patterns is the closest sibling in structure — both sets recompute a full pixel grid every frame from a small rule — but a life grid decides each cell from its neighbors' state, while every pattern above decides a pixel from raw distance to a seed. For the pixel-buffer method these nine actually write through, createImageData and putImageData are the pair in use here; MDN's getImageData reference documents that pair's read-side sibling, for reading a canvas back out instead of writing one in.

Questions Worth Answering Before You Copy a Voronoi Diagram Generator

Are these nine canvases actually computing a voronoi diagram, or just faking the look?

They're computing it — nearestTwo() runs a real squared-distance comparison against every seed for every pixel in the 160×100 calc canvas, and a pixel's color is decided purely by which seed is closest. Nothing here uses a gradient, a border-radius trick, or a pre-baked image to fake the cell shapes.

Can I use one of these nine as a voronoi generator svg starting point instead of canvas?

Not directly — every item here draws raw pixels into an ImageData buffer, not vector paths, so there's no <path> data to lift out. The seed placement, the nearest-seed rule, and the constants (VORONOI_SEED_COUNT, SEED_REPEL_RADIUS_PX, and so on) translate to an SVG version, but the pixel-fill code itself would need to be rewritten as polygon math.

Why does item 4 look like triangles instead of the polygon cells in the other eight?

Because it draws the Delaunay triangulation — the dual graph of a voronoi diagram — built from the same seed coordinates the other eight use for their cells. Where the other items ask "which seed is closest to this pixel," item 4 asks "which three seeds form a triangle whose circumcircle contains no other seed," which is a genuinely different calculation on the same input points.

Enter the archive password

The password is inside this article. You will find it as you read.