Where ideas percolate and thoughts brew

The Curation Trap

About This Sketch

A grid that oscillates between perfect curation and messy authenticity. Watch as the same underlying pattern transforms from Instagram-ready perfection to organic chaos and back again. This sketch accompanies the post "The Curation Trap" and explores how we've learned to filter every aspect of our lives into polished performances—and what we lose in the process.

Algorithm

This sketch visualizes the tension between curated perfection and authentic messiness. A grid of cells oscillates between two states: **Curated Mode:** Perfect geometric pattern, uniform squares, predictable checkerboard arrangement. Every cell is aligned, sized consistently, and follows a clear pattern. This represents the polished, Instagram-ready version of reality. **Authentic Mode:** Messy, inconsistent, organic. Circles instead of squares, varying sizes, random brightness and positioning. Some cells are "messy" by nature, others follow their own rules. This represents unfiltered reality. The sketch smoothly transitions between these states every few seconds, showing how the same underlying grid (the same reality) can be presented in radically different ways. The transition reveals that curation isn't about changing what's there—it's about how it's presented.

Pseudocode

SETUP:
  Create grid of cells (20x15)
  Each cell has:
    - Position in grid
    - "Messy" flag (random)
    - Brightness value (random)
    - Size variation (random)

DRAW (every frame):
  Get current theme colors
  Update transition timer
  Calculate filter amount (0 = authentic, 1 = curated)

  For each cell in grid:
    Calculate "curated" appearance:
      - Uniform checkerboard pattern
      - Perfect square shape
      - Standard size

    Calculate "authentic" appearance:
      - Random brightness/messiness
      - Organic circle shape
      - Varying sizes

    Interpolate between the two based on filter amount
    Draw the cell

  Display label ("curated" or "authentic")

  Toggle between states every 3 seconds

METAPHOR:
  Same grid, same underlying data, drastically different presentations.
  Curation is a filter over reality, not reality itself.

Source Code

let sketch = function(p) {
    let grid = [];
    let gridSize = 20;
    let cols, rows;
    let time = 0;
    let showFiltered = true;
    let transitionTime = 0;
    let transitionDuration = 180;

    class Cell {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.rawValue = p.noise(x * 0.1, y * 0.1, 0) * 255;
            this.messy = p.random(0, 1) > 0.5;
            this.brightness = p.random(100, 255);
            this.size = p.random(0.7, 1.3);
        }

        getFilteredValue(colors) {
            let pattern = Math.floor((this.x + this.y) / 2) % 2;
            return pattern === 0 ? colors.accent1 : colors.accent2;
        }

        getRawValue(colors) {
            if (this.messy) {
                return colors.accent3;
            } else {
                return this.brightness > 180 ? colors.accent1 : colors.accent2;
            }
        }

        display(colors, filterAmount) {
            let filtered = this.getFilteredValue(colors);
            let raw = this.getRawValue(colors);

            let r = p.lerp(raw[0], filtered[0], filterAmount);
            let g = p.lerp(raw[1], filtered[1], filterAmount);
            let b = p.lerp(raw[2], filtered[2], filterAmount);

            p.fill(r, g, b);
            p.noStroke();

            let cellSize = gridSize * p.lerp(this.size, 1.0, filterAmount);
            let xPos = this.x * gridSize + gridSize / 2;
            let yPos = this.y * gridSize + gridSize / 2;

            if (filterAmount > 0.5) {
                p.rect(xPos - cellSize / 2, yPos - cellSize / 2, cellSize, cellSize);
            } else {
                p.circle(xPos, yPos, cellSize);
            }
        }
    }

    p.setup = function() {
        p.createCanvas(400, 300);
        cols = Math.floor(400 / gridSize);
        rows = Math.floor(300 / gridSize);

        for (let i = 0; i < cols; i++) {
            grid[i] = [];
            for (let j = 0; j < rows; j++) {
                grid[i][j] = new Cell(i, j);
            }
        }
    };

    p.draw = function() {
        const colors = getThemeColors();
        p.background(...colors.bg);

        time += 0.01;

        transitionTime += 1;
        if (transitionTime > transitionDuration * 2) {
            transitionTime = 0;
            showFiltered = !showFiltered;
        }

        let targetFilter = showFiltered ? 1.0 : 0.0;
        let currentPhase = (transitionTime % (transitionDuration * 2)) / transitionDuration;

        let filterAmount;
        if (currentPhase < 1) {
            filterAmount = showFiltered ? currentPhase : 1 - currentPhase;
        } else {
            filterAmount = targetFilter;
        }

        filterAmount = p.constrain(filterAmount, 0, 1);
        filterAmount = filterAmount * filterAmount * (3 - 2 * filterAmount);

        for (let i = 0; i < cols; i++) {
            for (let j = 0; j < rows; j++) {
                grid[i][j].display(colors, filterAmount);
            }
        }

        p.fill(...colors.accent3, 150);
        p.noStroke();
        p.textSize(11);
        p.textAlign(p.LEFT);

        if (filterAmount > 0.5) {
            p.text('curated', 10, 20);
        } else {
            p.text('authentic', 10, 20);
        }
    };
};