Where ideas percolate and thoughts brew

The Coziness Industrial Complex

About This Sketch

A visualization contrasting performed coziness with natural comfort. Complex, hollow geometric shapes with dollar signs represent the coziness industrial complex—expensive, busy, and actually stress-inducing. Simple, solid, warmly-glowing shapes represent natural comfort—cheap, lasting, and genuinely comfortable. Watch the complexity meter rise as performed coziness accumulates, while actual comfort comes from simpler sources. The 75/25 ratio reflects modern reality: we're drowning in aesthetic performance of comfort while losing access to simple, natural ease.

Algorithm

This sketch visualizes the contrast between performed coziness and natural comfort through two types of objects that accumulate on screen. Performed coziness items (75% of spawns) are represented as hollow, complex geometric shapes with multiple overlapping patterns, dollar signs, and rapid spinning. They contribute to a "complexity" meter and have negative comfort values—actually adding stress rather than comfort. They're visually busy, short-lived, and fade quickly. Natural comfort items (25% of spawns) appear as simple, solid polygons with warm glowing centers and gentle pulsing. They contribute positively to an "actual comfort" meter, last much longer, and create subtle ripple effects showing lasting impact. They rotate slowly and peacefully. The sketch tracks and displays both "Actual Comfort" and "Complexity" levels in real-time bars, making visible how the accumulation of performed coziness increases stress while simple comfort creates genuine ease. The 75/25 ratio reflects how performed coziness has overtaken natural comfort in modern culture. This accompanies the blog post "The Coziness Industrial Complex" which explores how comfort has been turned into a consumption aesthetic that's actually uncomfortable.

Pseudocode

SETUP:
  Initialize canvas (400x300)
  Create empty array for cozy items

DRAW (every frame):
  Get current theme colors
  Semi-transparent background (creates trailing effect)
  Draw title

  SPAWN NEW ITEMS (4% chance per frame):
    75% chance: Create performed coziness item
      - Complex (8-16 sided geometric)
      - Hollow with overlapping shapes
      - High complexity value (15-30)
      - Fast rotation and pulsing
      - Short lifespan (30-60 frames)
      - Negative comfort impact
    25% chance: Create natural comfort item
      - Simple (3-6 sided polygon)
      - Solid with warm glow
      - Low complexity value (1-5)
      - Slow rotation and gentle pulse
      - Long lifespan (200-400 frames)
      - High positive comfort impact

  UPDATE ALL ITEMS:
    Age each item
    Apply rotation and pulsing
    Fade out near end of lifespan
    Remove if lifespan exceeded

  DISPLAY ALL ITEMS:
    For performed items:
      Draw multiple overlapping geometric shapes
      Add tiny center (low actual comfort)
      Show dollar sign (expensive)
    For natural items:
      Draw simple solid shape
      Add warm inner glow
      Create gentle ripple effect (lasting impact)

  CALCULATE METRICS:
    Sum complexity from performed items
    Sum comfort from natural items
    Display bars showing levels

  DRAW LEGEND:
    Show example of each type
    Explain what you're seeing
    Add bottom note about observation

Source Code

let sketch = function(p) {
    let cozyItems = [];
    let maxItems = 50;
    let stressLevel = 0;

    // Two types: natural comfort vs performed coziness
    class CozyItem {
        constructor(x, y, isPerformed) {
            this.x = x;
            this.y = y;
            this.isPerformed = isPerformed;
            this.age = 0;

            if (isPerformed) {
                // Performed coziness: lots of items, complex, stressful
                this.comfortValue = p.random(-0.2, 0.1); // Usually adds stress
                this.complexity = p.random(15, 30); // High complexity
                this.cost = p.random(50, 200); // Expensive
                this.size = p.random(6, 12);
                this.rotationSpeed = p.random(0.02, 0.05); // Busy
                this.pulseSpeed = p.random(0.05, 0.1); // Demanding attention
                this.lifespan = p.random(30, 60); // Short-lived trend
                this.numLines = Math.floor(p.random(8, 16)); // Complex shape
            } else {
                // Natural comfort: simple, effective, cheap
                this.comfortValue = p.random(0.5, 1.0); // Actually comfortable
                this.complexity = p.random(1, 5); // Simple
                this.cost = p.random(0, 20); // Cheap or free
                this.size = p.random(8, 15); // Substantial but simple
                this.rotationSpeed = p.random(0.001, 0.01); // Calm
                this.pulseSpeed = p.random(0.01, 0.02); // Gentle
                this.lifespan = p.random(200, 400); // Long-lasting
                this.numLines = Math.floor(p.random(3, 6)); // Simple shape
            }

            this.rotation = p.random(p.TWO_PI);
            this.pulsePhase = p.random(p.TWO_PI);
            this.alpha = 255;
        }

        update() {
            this.age++;
            this.rotation += this.rotationSpeed;
            this.pulsePhase += this.pulseSpeed;

            // Fade out as lifespan ends
            if (this.age > this.lifespan * 0.7) {
                this.alpha = p.map(this.age, this.lifespan * 0.7, this.lifespan, 255, 0);
            }

            return this.age < this.lifespan;
        }

        display(colors) {
            p.push();
            p.translate(this.x, this.y);
            p.rotate(this.rotation);

            let pulse = p.sin(this.pulsePhase) * 0.15 + 1;
            let displaySize = this.size * pulse;

            if (this.isPerformed) {
                // Performed coziness: Complex, hollow, geometric
                p.noFill();
                p.stroke(...colors.accent3, this.alpha * 0.8);
                p.strokeWeight(1.5);

                // Multiple overlapping shapes (complexity)
                for (let i = 0; i < 3; i++) {
                    p.push();
                    p.rotate((p.TWO_PI / 3) * i);

                    p.beginShape();
                    for (let j = 0; j < this.numLines; j++) {
                        let angle = (p.TWO_PI / this.numLines) * j;
                        let r = displaySize * (0.5 + i * 0.2);
                        let px = p.cos(angle) * r;
                        let py = p.sin(angle) * r;
                        p.vertex(px, py);
                    }
                    p.endShape(p.CLOSE);
                    p.pop();
                }

                // Tiny core (low actual comfort)
                p.fill(...colors.accent3, this.alpha * 0.3);
                p.noStroke();
                p.circle(0, 0, displaySize * 0.2);

                // Price tag indicator (dollar signs)
                p.fill(...colors.accent2, this.alpha * 0.5);
                p.textSize(6);
                p.textAlign(p.CENTER, p.CENTER);
                p.text('$', 0, -displaySize * 0.7);

            } else {
                // Natural comfort: Simple, solid, warm
                p.fill(...colors.accent1, this.alpha * 0.7);
                p.noStroke();

                // Simple shape
                p.beginShape();
                for (let j = 0; j < this.numLines; j++) {
                    let angle = (p.TWO_PI / this.numLines) * j;
                    let r = displaySize;
                    let px = p.cos(angle) * r;
                    let py = p.sin(angle) * r;
                    p.vertex(px, py);
                }
                p.endShape(p.CLOSE);

                // Warm glow (actual comfort)
                p.fill(...colors.accent2, this.alpha * 0.5);
                p.circle(0, 0, displaySize * 0.6);

                // Gentle ripple of lasting comfort
                if (this.age > 20) {
                    let rippleSize = p.map(p.sin(this.age * 0.02), -1, 1, displaySize * 1.2, displaySize * 1.5);
                    let rippleAlpha = this.alpha * 0.2;
                    p.noFill();
                    p.stroke(...colors.accent1, rippleAlpha);
                    p.strokeWeight(1);
                    p.circle(0, 0, rippleSize);
                }
            }

            p.pop();
        }
    }

    p.setup = function() {
        p.createCanvas(400, 300);
    };

    p.draw = function() {
        const colors = getThemeColors();
        p.background(...colors.bg, 60); // Trails

        // Title
        p.fill(...colors.accent3);
        p.noStroke();
        p.textAlign(p.CENTER);
        p.textSize(11);
        p.text('The Coziness Industrial Complex', 200, 18);

        // Generate new items
        // 75% performed (reflects reality)
        if (p.random() < 0.04 && cozyItems.length < maxItems) {
            let isPerformed = p.random() < 0.75;
            let x = p.random(50, 350);
            let y = p.random(50, 250);
            cozyItems.push(new CozyItem(x, y, isPerformed));
        }

        // Calculate cumulative stress
        stressLevel = 0;
        let comfortLevel = 0;
        for (let item of cozyItems) {
            if (item.isPerformed) {
                stressLevel += item.complexity * 0.1;
            } else {
                comfortLevel += item.comfortValue * 2;
            }
        }

        // Update and display items
        for (let i = cozyItems.length - 1; i >= 0; i--) {
            if (!cozyItems[i].update()) {
                cozyItems.splice(i, 1);
            } else {
                cozyItems[i].display(colors);
            }
        }

        // Stress/Comfort meter
        p.textAlign(p.LEFT);
        p.textSize(8);
        p.fill(...colors.accent3);
        p.text('Actual Comfort:', 15, 270);
        p.text('Complexity:', 15, 285);

        // Comfort bar (goes up with simple items)
        let comfortWidth = p.constrain(comfortLevel * 8, 0, 100);
        p.fill(...colors.accent1, 150);
        p.noStroke();
        p.rect(95, 263, comfortWidth, 8, 2);

        // Stress bar (goes up with performed items)
        let stressWidth = p.constrain(stressLevel, 0, 100);
        p.fill(...colors.accent3, 150);
        p.rect(95, 278, stressWidth, 8, 2);

        // Legend
        p.textSize(7);

        // Performed (hollow geometric)
        p.push();
        p.translate(220, 268);
        p.noFill();
        p.stroke(...colors.accent3, 200);
        p.strokeWeight(1);
        for (let i = 0; i < 6; i++) {
            let angle = (p.TWO_PI / 6) * i;
            let px = p.cos(angle) * 5;
            let py = p.sin(angle) * 5;
            if (i === 0) p.beginShape();
            p.vertex(px, py);
            if (i === 5) p.endShape(p.CLOSE);
        }
        p.pop();
        p.fill(...colors.accent3);
        p.noStroke();
        p.text('Performed: Complex, expensive, stressful', 230, 271);

        // Natural (simple solid)
        p.fill(...colors.accent1, 180);
        p.circle(225, 283, 8);
        p.fill(...colors.accent2, 120);
        p.circle(225, 283, 5);
        p.fill(...colors.accent3);
        p.text('Natural: Simple, cheap, actually comfortable', 230, 286);

        // Bottom note
        p.fill(...colors.accent3, 130);
        p.textAlign(p.CENTER);
        p.textSize(7);
        p.text('Watch complexity rise. Notice what actually feels good.', 200, 297);
    };
};