Where ideas percolate and thoughts brew

The Ritual Trap

About This Sketch

Visualizing the ritual trap Top: Perfect, geometric ritual forms - rigid, repetitive, empty Bottom: Organic, irregular moments of genuine presence - messy but alive Shows contrast between performed ritual and actual engagement

This sketch accompanies the blog post "The Ritual Trap" and visualizes its core concepts through generative art.

Algorithm

Visualizing the ritual trap Top: Perfect, geometric ritual forms - rigid, repetitive, empty Bottom: Organic, irregular moments of genuine presence - messy but alive Shows contrast between performed ritual and actual engagement This sketch was originally created as a visual companion to the blog post "The Ritual Trap" and explores its themes through generative art.

Pseudocode

SETUP:
  Initialize canvas (400x300)
  Set up drawing parameters

DRAW (every frame):
  Get current theme colors
  Clear background
  Draw generative visualization
  Update animation state

Source Code

let sketch = function(p) {

    // Visualizing the ritual trap
    // Top: Perfect, geometric ritual forms - rigid, repetitive, empty
    // Bottom: Organic, irregular moments of genuine presence - messy but alive
    // Shows contrast between performed ritual and actual engagement

    let time = 0;
    let ritualForms = [];
    let genuineMoments = [];

    class RitualForm {
        constructor(x, y, type) {
            this.x = x;
            this.y = y;
            this.type = type; // 0: circle, 1: square, 2: triangle
            this.size = 25;
            this.rotation = 0;
            this.rotationSpeed = 0.02;
            this.alpha = 180;
            this.empty = true; // Hollow, just form
        }

        update() {
            // Mechanical, predictable rotation
            this.rotation += this.rotationSpeed;
        }

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

            // Hollow forms - all structure, no substance
            p.noFill();
            p.stroke(...colors.accent3, this.alpha);
            p.strokeWeight(2);

            if (this.type === 0) {
                p.circle(0, 0, this.size);
            } else if (this.type === 1) {
                p.square(-this.size/2, -this.size/2, this.size);
            } else {
                p.triangle(0, -this.size/2, -this.size/2, this.size/2, this.size/2, this.size/2);
            }

            // Checkmark inside - ritual completed
            if (time % 120 > 60) {
                let checkAlpha = p.map(time % 60, 0, 60, 0, 150);
                p.stroke(...colors.accent3, checkAlpha);
                p.strokeWeight(1.5);
                p.noFill();
                p.beginShape();
                p.vertex(-5, 0);
                p.vertex(-2, 5);
                p.vertex(6, -6);
                p.endShape();
            }

            p.pop();
        }
    }

    class GenuineMoment {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.life = 255;
            this.decay = p.random(1, 3);
            this.size = p.random(8, 20);
            this.vx = p.random(-0.5, 0.5);
            this.vy = p.random(-1, -2);
            this.color = p.random([0, 1]); // accent1 or accent2
            this.rotation = p.random(p.TWO_PI);
            this.rotationSpeed = p.random(-0.05, 0.05);
            this.shape = p.floor(p.random(4)); // More variety in shapes
        }

        update() {
            // Organic, unpredictable movement
            this.x += this.vx;
            this.y += this.vy;
            this.life -= this.decay;
            this.rotation += this.rotationSpeed;

            // Slow down
            this.vy += 0.03;
            this.vx *= 0.98;
        }

        display(colors) {
            if (this.life <= 0) return;

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

            // Filled, alive, varied shapes - substance not just form
            let col = this.color === 0 ? colors.accent1 : colors.accent2;
            p.fill(...col, this.life);
            p.noStroke();

            // Irregular, organic shapes
            if (this.shape === 0) {
                // Irregular blob
                p.beginShape();
                for (let a = 0; a < p.TWO_PI; a += p.PI/4) {
                    let r = this.size/2 + p.sin(a * 3 + time * 0.05) * 3;
                    p.vertex(p.cos(a) * r, p.sin(a) * r);
                }
                p.endShape(p.CLOSE);
            } else if (this.shape === 1) {
                // Splatter
                for (let i = 0; i < 5; i++) {
                    let angle = (p.TWO_PI / 5) * i;
                    let r = this.size/2;
                    p.circle(p.cos(angle) * r, p.sin(angle) * r, this.size/3);
                }
            } else if (this.shape === 2) {
                // Streak
                p.ellipse(0, 0, this.size, this.size/2);
            } else {
                // Cluster
                p.circle(0, 0, this.size);
                p.circle(this.size/3, this.size/3, this.size/2);
            }

            p.pop();
        }

        isDead() {
            return this.life <= 0;
        }
    }

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

        // Initialize ritual forms (top) - perfectly spaced, mechanical
        for (let i = 0; i < 5; i++) {
            ritualForms.push(
                new RitualForm(
                    60 + i * 70,
                    60,
                    i % 3
                )
            );
        }
    };

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

        // Title
        p.noStroke();
        p.fill(...colors.accent3, 180);
        p.textAlign(p.CENTER, p.TOP);
        p.textSize(11);
        p.text("The Ritual Trap", 200, 15);

        // Section labels
        p.textAlign(p.CENTER, p.TOP);
        p.textSize(8);
        p.fill(...colors.accent3, 160);
        p.text("Performed ritual (empty form)", 200, 35);
        p.text("Genuine engagement (messy substance)", 200, 145);

        // Dividing line
        p.stroke(...colors.accent3, 60);
        p.strokeWeight(1);
        p.line(30, 130, 370, 130);

        // Top: Ritual forms - mechanical, perfect, empty
        for (let form of ritualForms) {
            form.update();
            form.display(colors);
        }

        // Show repetition of ritual
        if (time > 60) {
            let repeatAlpha = Math.min((time - 60) * 2, 140);
            p.noStroke();
            p.fill(...colors.accent3, repeatAlpha);
            p.textAlign(p.CENTER, p.CENTER);
            p.textSize(7);
            p.text("Perfect. Consistent. Empty.", 200, 95);
        }

        // Add annotations about optimization
        if (time > 180 && time < 300) {
            let annAlpha = Math.min((time - 180) * 2, 130);
            p.textSize(6);
            p.fill(...colors.accent3, annAlpha);
            p.textAlign(p.LEFT, p.CENTER);
            p.text("□ Morning meditation", 40, 110);
            p.text("□ Gratitude journal", 150, 110);
            p.text("□ Intention setting", 260, 110);
        }

        // Bottom: Genuine moments - irregular, unpredictable, alive
        for (let moment of genuineMoments) {
            moment.update();
            moment.display(colors);
        }

        // Clean up dead moments
        genuineMoments = genuineMoments.filter(m => !m.isDead());

        // Randomly spawn genuine moments - irregular timing
        if (p.random() < 0.05) {
            genuineMoments.push(
                new GenuineMoment(
                    p.random(60, 340),
                    240
                )
            );
        }

        // Comparison messages
        if (time > 300) {
            let msgAlpha = Math.min((time - 300) * 1.5, 160);

            p.textAlign(p.LEFT, p.CENTER);
            p.textSize(7);
            p.noStroke();
            p.fill(...colors.accent3, msgAlpha);

            p.text("Top: Checking boxes", 30, 80);
            p.text("Bottom: Actually feeling", 30, 200);
        }

        // Show the problem
        if (time > 420) {
            let problemAlpha = Math.min((time - 420) * 1.5, 170);

            p.textAlign(p.CENTER, p.BOTTOM);
            p.textSize(8);
            p.fill(...colors.accent2, problemAlpha);
            p.text("The protocol replaced the presence", 200, 275);
        }

        // Final insight
        if (time > 540) {
            let insightAlpha = Math.min((time - 540) * 2, 160);

            p.textAlign(p.CENTER, p.BOTTOM);
            p.textSize(8);
            p.fill(...colors.accent1, insightAlpha);
            p.text("Real moments are irregular, imperfect, and can't be scheduled", 200, 293);
        }

        // Occasionally show ritual becoming obligation
        if (time > 200 && time % 180 < 60) {
            let obligAlpha = p.map(time % 60, 0, 60, 0, 140);

            // "SHOULD" appearing over ritual forms
            p.push();
            p.textAlign(p.CENTER, p.CENTER);
            p.textSize(10);
            p.fill(...colors.accent3, obligAlpha);
            p.text("SHOULD", 200, 60);
            p.pop();
        }

        time++;

        // Loop
        if (time > 700) {
            time = 0;
        }
    };
};