diff --git a/db/init.sql b/db/init.sql
index 798a6e1..bfd5109 100644
--- a/db/init.sql
+++ b/db/init.sql
@@ -16,6 +16,7 @@ CREATE TABLE users (
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user', -- user, moderator, admin
trust_tier TEXT NOT NULL DEFAULT 'standard', -- standard, creator, trusted_api
+ is_system BOOLEAN NOT NULL DEFAULT FALSE, -- platform system account (fractafrag)
stripe_customer_id TEXT,
subscription_tier TEXT DEFAULT 'free', -- free, pro, studio
ai_credits_remaining INTEGER DEFAULT 0,
@@ -38,21 +39,25 @@ CREATE TABLE shaders (
title TEXT NOT NULL,
description TEXT,
glsl_code TEXT NOT NULL,
+ status TEXT NOT NULL DEFAULT 'published', -- draft, published, archived
is_public BOOLEAN DEFAULT TRUE,
is_ai_generated BOOLEAN DEFAULT FALSE,
- ai_provider TEXT, -- anthropic, openai, ollama, null
+ is_system BOOLEAN DEFAULT FALSE, -- generated by fractafrag platform
+ ai_provider TEXT, -- anthropic, openai, ollama, null
+ system_label TEXT, -- e.g. 'fractafrag-curated', 'fractafrag-generated'
thumbnail_url TEXT,
preview_url TEXT,
- render_status TEXT DEFAULT 'pending', -- pending, rendering, ready, failed
- style_vector vector(512), -- pgvector: visual style embedding
- style_metadata JSONB, -- { chaos_level, color_temp, motion_type, ... }
+ render_status TEXT DEFAULT 'pending', -- pending, rendering, ready, failed
+ style_vector vector(512), -- pgvector: visual style embedding
+ style_metadata JSONB, -- { chaos_level, color_temp, motion_type, ... }
tags TEXT[],
- shader_type TEXT DEFAULT '2d', -- 2d, 3d, audio-reactive
+ shader_type TEXT DEFAULT '2d', -- 2d, 3d, audio-reactive
forked_from UUID REFERENCES shaders(id) ON DELETE SET NULL,
+ current_version INTEGER NOT NULL DEFAULT 1, -- current version number
view_count INTEGER DEFAULT 0,
- score FLOAT DEFAULT 0, -- cached hot score for feed ranking
+ score FLOAT DEFAULT 0, -- cached hot score for feed ranking
-- Creator economy stubs (Section 11f)
- access_tier TEXT DEFAULT 'open', -- open, source_locked, commercial
+ access_tier TEXT DEFAULT 'open',
source_unlock_price_cents INTEGER,
commercial_license_price_cents INTEGER,
verified_creator_shader BOOLEAN DEFAULT FALSE,
@@ -61,15 +66,33 @@ CREATE TABLE shaders (
updated_at TIMESTAMPTZ DEFAULT NOW()
);
+-- ════════════════════════════════════════════════════════════
+-- SHADER VERSIONS — immutable snapshots of each edit
+-- ════════════════════════════════════════════════════════════
+CREATE TABLE shader_versions (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ shader_id UUID NOT NULL REFERENCES shaders(id) ON DELETE CASCADE,
+ version_number INTEGER NOT NULL,
+ glsl_code TEXT NOT NULL,
+ title TEXT NOT NULL,
+ description TEXT,
+ tags TEXT[],
+ style_metadata JSONB,
+ change_note TEXT, -- optional: "fixed the color bleeding", "added mouse interaction"
+ thumbnail_url TEXT,
+ created_at TIMESTAMPTZ DEFAULT NOW(),
+ UNIQUE (shader_id, version_number)
+);
+
-- ════════════════════════════════════════════════════════════
-- VOTES
-- ════════════════════════════════════════════════════════════
CREATE TABLE votes (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- user_id UUID REFERENCES users(id) ON DELETE CASCADE,
- shader_id UUID REFERENCES shaders(id) ON DELETE CASCADE,
- value SMALLINT NOT NULL CHECK (value IN (-1, 1)),
- created_at TIMESTAMPTZ DEFAULT NOW(),
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ user_id UUID REFERENCES users(id) ON DELETE CASCADE,
+ shader_id UUID REFERENCES shaders(id) ON DELETE CASCADE,
+ value SMALLINT NOT NULL CHECK (value IN (-1, 1)),
+ created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (user_id, shader_id)
);
@@ -78,10 +101,10 @@ CREATE TABLE votes (
-- ════════════════════════════════════════════════════════════
CREATE TABLE engagement_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- user_id UUID REFERENCES users(id) ON DELETE SET NULL, -- null for anonymous
- session_id TEXT, -- anonymous session token
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
+ session_id TEXT,
shader_id UUID REFERENCES shaders(id) ON DELETE CASCADE,
- event_type TEXT NOT NULL, -- dwell, replay, share, generate_similar
+ event_type TEXT NOT NULL,
dwell_secs FLOAT,
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
@@ -91,21 +114,20 @@ CREATE TABLE engagement_events (
-- DESIRES / BOUNTIES
-- ════════════════════════════════════════════════════════════
CREATE TABLE desires (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- author_id UUID REFERENCES users(id) ON DELETE SET NULL,
- prompt_text TEXT NOT NULL,
- prompt_embedding vector(512), -- embedded for similarity grouping
- style_hints JSONB, -- { chaos_level, color_temp, etc }
- tip_amount_cents INTEGER DEFAULT 0,
- status TEXT DEFAULT 'open', -- open, in_progress, fulfilled, expired
- heat_score FLOAT DEFAULT 1, -- updated as similar desires accumulate
- fulfilled_by_shader UUID REFERENCES shaders(id) ON DELETE SET NULL,
- fulfilled_at TIMESTAMPTZ,
- expires_at TIMESTAMPTZ,
- created_at TIMESTAMPTZ DEFAULT NOW()
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ author_id UUID REFERENCES users(id) ON DELETE SET NULL,
+ prompt_text TEXT NOT NULL,
+ prompt_embedding vector(512),
+ style_hints JSONB,
+ tip_amount_cents INTEGER DEFAULT 0,
+ status TEXT DEFAULT 'open',
+ heat_score FLOAT DEFAULT 1,
+ fulfilled_by_shader UUID REFERENCES shaders(id) ON DELETE SET NULL,
+ fulfilled_at TIMESTAMPTZ,
+ expires_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ DEFAULT NOW()
);
--- Similar desire grouping (many-to-many)
CREATE TABLE desire_clusters (
cluster_id UUID,
desire_id UUID REFERENCES desires(id) ON DELETE CASCADE,
@@ -117,27 +139,27 @@ CREATE TABLE desire_clusters (
-- BOUNTY TIPS
-- ════════════════════════════════════════════════════════════
CREATE TABLE bounty_tips (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- desire_id UUID REFERENCES desires(id) ON DELETE CASCADE,
- tipper_id UUID REFERENCES users(id) ON DELETE SET NULL,
- amount_cents INTEGER NOT NULL,
- stripe_payment_intent_id TEXT,
- status TEXT DEFAULT 'held', -- held, released, refunded
- created_at TIMESTAMPTZ DEFAULT NOW()
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ desire_id UUID REFERENCES desires(id) ON DELETE CASCADE,
+ tipper_id UUID REFERENCES users(id) ON DELETE SET NULL,
+ amount_cents INTEGER NOT NULL,
+ stripe_payment_intent_id TEXT,
+ status TEXT DEFAULT 'held',
+ created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ════════════════════════════════════════════════════════════
-- CREATOR PAYOUTS
-- ════════════════════════════════════════════════════════════
CREATE TABLE creator_payouts (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- creator_id UUID REFERENCES users(id) ON DELETE SET NULL,
- desire_id UUID REFERENCES desires(id) ON DELETE SET NULL,
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ creator_id UUID REFERENCES users(id) ON DELETE SET NULL,
+ desire_id UUID REFERENCES desires(id) ON DELETE SET NULL,
gross_amount_cents INTEGER,
- platform_fee_cents INTEGER, -- 10%
- net_amount_cents INTEGER, -- 90%
+ platform_fee_cents INTEGER,
+ net_amount_cents INTEGER,
stripe_transfer_id TEXT,
- status TEXT DEFAULT 'pending', -- pending, processing, completed, failed
+ status TEXT DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT NOW()
);
@@ -145,17 +167,17 @@ CREATE TABLE creator_payouts (
-- API KEYS (for MCP clients)
-- ════════════════════════════════════════════════════════════
CREATE TABLE api_keys (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- user_id UUID REFERENCES users(id) ON DELETE CASCADE,
- key_hash TEXT UNIQUE NOT NULL, -- bcrypt hash of the actual key
- key_prefix TEXT NOT NULL, -- first 8 chars for display (ff_key_XXXXXXXX)
- name TEXT, -- user-given label
- trust_tier TEXT DEFAULT 'probation', -- probation, trusted, premium
- submissions_approved INTEGER DEFAULT 0,
- rate_limit_per_hour INTEGER DEFAULT 10,
- last_used_at TIMESTAMPTZ,
- created_at TIMESTAMPTZ DEFAULT NOW(),
- revoked_at TIMESTAMPTZ
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ user_id UUID REFERENCES users(id) ON DELETE CASCADE,
+ key_hash TEXT UNIQUE NOT NULL,
+ key_prefix TEXT NOT NULL,
+ name TEXT,
+ trust_tier TEXT DEFAULT 'probation',
+ submissions_approved INTEGER DEFAULT 0,
+ rate_limit_per_hour INTEGER DEFAULT 10,
+ last_used_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ DEFAULT NOW(),
+ revoked_at TIMESTAMPTZ
);
-- ════════════════════════════════════════════════════════════
@@ -168,7 +190,7 @@ CREATE TABLE generation_log (
provider TEXT NOT NULL,
prompt_text TEXT,
tokens_used INTEGER,
- cost_cents INTEGER, -- platform cost for credit-based generations
+ cost_cents INTEGER,
success BOOLEAN,
created_at TIMESTAMPTZ DEFAULT NOW()
);
@@ -192,7 +214,7 @@ CREATE TABLE source_unlocks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
shader_id UUID REFERENCES shaders(id) ON DELETE CASCADE,
buyer_id UUID REFERENCES users(id) ON DELETE SET NULL,
- license_type TEXT NOT NULL, -- personal, commercial
+ license_type TEXT NOT NULL,
amount_cents INTEGER NOT NULL,
platform_fee_cents INTEGER NOT NULL,
stripe_payment_intent_id TEXT,
@@ -215,22 +237,18 @@ CREATE TABLE creator_engagement_snapshots (
-- ════════════════════════════════════════════════════════════
-- Feed performance
-CREATE INDEX idx_shaders_score ON shaders(score DESC) WHERE is_public = TRUE;
-CREATE INDEX idx_shaders_created ON shaders(created_at DESC) WHERE is_public = TRUE;
+CREATE INDEX idx_shaders_score ON shaders(score DESC) WHERE is_public = TRUE AND status = 'published';
+CREATE INDEX idx_shaders_created ON shaders(created_at DESC) WHERE is_public = TRUE AND status = 'published';
CREATE INDEX idx_shaders_tags ON shaders USING GIN(tags);
CREATE INDEX idx_shaders_render_status ON shaders(render_status) WHERE render_status != 'ready';
+CREATE INDEX idx_shaders_status ON shaders(status);
+CREATE INDEX idx_shaders_author_status ON shaders(author_id, status, updated_at DESC);
+CREATE INDEX idx_shaders_system ON shaders(is_system) WHERE is_system = TRUE;
--- Recommendation (pgvector ANN — ivfflat, will rebuild after data exists)
--- NOTE: ivfflat indexes require data in the table to build properly.
--- Run these AFTER seeding initial data:
--- CREATE INDEX idx_shaders_style_vector ON shaders
--- USING ivfflat (style_vector vector_cosine_ops) WITH (lists = 100);
--- CREATE INDEX idx_users_taste_vector ON users
--- USING ivfflat (taste_vector vector_cosine_ops) WITH (lists = 50);
--- CREATE INDEX idx_desires_embedding ON desires
--- USING ivfflat (prompt_embedding vector_cosine_ops) WITH (lists = 50);
+-- Versioning
+CREATE INDEX idx_shader_versions_shader ON shader_versions(shader_id, version_number DESC);
--- For now, use HNSW (works on empty tables, better perf at small scale)
+-- Recommendation (pgvector HNSW — works on empty tables)
CREATE INDEX idx_shaders_style_vector ON shaders
USING hnsw (style_vector vector_cosine_ops) WITH (m = 16, ef_construction = 64);
CREATE INDEX idx_users_taste_vector ON users
@@ -263,3 +281,20 @@ CREATE INDEX idx_comments_parent ON comments(parent_id);
-- Text search
CREATE INDEX idx_shaders_title_trgm ON shaders USING GIN(title gin_trgm_ops);
CREATE INDEX idx_desires_prompt_trgm ON desires USING GIN(prompt_text gin_trgm_ops);
+
+-- ════════════════════════════════════════════════════════════
+-- SYSTEM ACCOUNT: The "fractafrag" platform user
+-- All system-generated/curated shaders are authored by this account
+-- ════════════════════════════════════════════════════════════
+INSERT INTO users (id, username, email, password_hash, role, trust_tier, is_system, subscription_tier, is_verified_creator)
+VALUES (
+ '00000000-0000-0000-0000-000000000001',
+ 'fractafrag',
+ 'system@fractafrag.local',
+ '$2b$12$000000000000000000000000000000000000000000000000000000', -- not a valid login
+ 'admin',
+ 'trusted_api',
+ TRUE,
+ 'studio',
+ TRUE
+);
diff --git a/scripts/seed_shaders.py b/scripts/seed_shaders.py
new file mode 100644
index 0000000..d4e3d17
--- /dev/null
+++ b/scripts/seed_shaders.py
@@ -0,0 +1,2043 @@
+"""
+Fractafrag Seed Data — 200+ high-quality GLSL shaders.
+
+Each shader is:
+- Written for the 'fractafrag' system account
+- Tagged comprehensively for search/filter/algorithm testing
+- Flagged as is_system=True with system_label='fractafrag-curated'
+- Covers 2D and 3D, every visual style imaginable
+- Ready for immediate display in the feed
+
+Usage:
+ docker compose exec api python /app/scripts/seed_shaders.py
+"""
+
+import asyncio
+import uuid
+import random
+from datetime import datetime, timedelta, timezone
+
+# System account UUID
+SYSTEM_USER_ID = "00000000-0000-0000-0000-000000000001"
+
+SHADERS = []
+
+def s(title, code, tags, shader_type="2d", description="", style_metadata=None):
+ """Helper to register a shader."""
+ SHADERS.append({
+ "title": title,
+ "code": code.strip(),
+ "tags": tags,
+ "shader_type": shader_type,
+ "description": description,
+ "style_metadata": style_metadata or {},
+ })
+
+# ═══════════════════════════════════════════════════════════
+# 2D — MATHEMATICAL / GEOMETRIC
+# ═══════════════════════════════════════════════════════════
+
+s("Spiral Galaxy", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float a = atan(uv.y, uv.x);
+ float r = length(uv);
+ float spiral = sin(a * 5.0 - r * 20.0 + iTime * 2.0);
+ float glow = 0.02 / (r + 0.01);
+ vec3 col = vec3(0.3, 0.1, 0.6) * glow + vec3(0.8, 0.4, 0.9) * spiral * exp(-r * 3.0);
+ fragColor = vec4(col, 1.0);
+}""", ["spiral", "galaxy", "mathematical", "glow", "purple"], "2d", "Logarithmic spiral arms with exponential glow falloff",
+{"chaos_level": 0.4, "color_temperature": "cool", "motion_type": "rotating"})
+
+s("Binary Rain", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float col = 0.0;
+ for (int i = 0; i < 12; i++) {
+ float fi = float(i);
+ vec2 p = fract(uv * vec2(20.0, 1.0) + vec2(fi * 0.37, iTime * (0.5 + fi * 0.1)));
+ float drop = smoothstep(0.0, 0.1, p.y) * smoothstep(1.0, 0.8, p.y);
+ col += drop * step(0.7, fract(sin(fi * 73.156) * 43758.5453));
+ }
+ fragColor = vec4(0.0, col * 0.8, col * 0.3, 1.0);
+}""", ["matrix", "rain", "digital", "code", "green", "cyberpunk"], "2d", "Digital rain effect inspired by the Matrix",
+{"chaos_level": 0.3, "color_temperature": "cool", "motion_type": "falling"})
+
+s("Voronoi Shatter", """
+vec2 hash2(vec2 p) {
+ p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
+ return fract(sin(p) * 43758.5453);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.y;
+ vec2 n = floor(uv * 6.0);
+ vec2 f = fract(uv * 6.0);
+ float md = 8.0;
+ vec2 mr;
+ for (int j = -1; j <= 1; j++)
+ for (int i = -1; i <= 1; i++) {
+ vec2 g = vec2(float(i), float(j));
+ vec2 o = hash2(n + g);
+ o = 0.5 + 0.5 * sin(iTime + 6.2831 * o);
+ vec2 r = g + o - f;
+ float d = dot(r, r);
+ if (d < md) { md = d; mr = r; }
+ }
+ vec3 col = 0.5 + 0.5 * cos(md * 6.0 + vec3(0, 1, 2) + iTime);
+ col *= 1.0 - 0.5 * smoothstep(0.0, 0.05, md);
+ fragColor = vec4(col, 1.0);
+}""", ["voronoi", "geometric", "mosaic", "cellular", "colorful", "animated"], "2d", "Animated Voronoi tessellation with iridescent cells",
+{"chaos_level": 0.5, "color_temperature": "warm", "motion_type": "morphing"})
+
+s("Monochrome Static Interference", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = floor(iTime * 15.0);
+ float n = hash(uv * 100.0 + t);
+ float scanline = sin(uv.y * 800.0 + iTime * 50.0) * 0.1;
+ float bars = step(0.98, fract(uv.y * 30.0 + iTime * 3.0)) * 0.3;
+ float v = n * 0.7 + scanline + bars;
+ v = clamp(v, 0.0, 1.0);
+ fragColor = vec4(vec3(v), 1.0);
+}""", ["static", "noise", "monochrome", "glitch", "tv", "analog", "bw"], "2d", "Analog TV static with scanlines and horizontal bars",
+{"chaos_level": 0.9, "color_temperature": "monochrome", "motion_type": "chaotic"})
+
+s("Mandelbrot Deep Zoom", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ float zoom = pow(2.0, -mod(iTime * 0.5, 30.0));
+ vec2 center = vec2(-0.7435669, 0.1314023);
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec2 c = center + uv * zoom;
+ vec2 z = vec2(0.0);
+ int iter = 0;
+ for (int i = 0; i < 256; i++) {
+ z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c;
+ if (dot(z, z) > 4.0) break;
+ iter = i;
+ }
+ float t = float(iter) / 256.0;
+ vec3 col = 0.5 + 0.5 * cos(3.0 + t * 15.0 + vec3(0.0, 0.6, 1.0));
+ if (iter == 255) col = vec3(0.0);
+ fragColor = vec4(col, 1.0);
+}""", ["mandelbrot", "fractal", "zoom", "mathematical", "infinite", "complex"], "2d", "Continuously zooming into the Mandelbrot set boundary",
+{"chaos_level": 0.6, "color_temperature": "cool", "motion_type": "zooming"})
+
+s("Neon Grid Pulse", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec2 grid = abs(fract(uv * 8.0 - 0.5) - 0.5);
+ float d = min(grid.x, grid.y);
+ float pulse = sin(iTime * 3.0 + length(uv) * 10.0) * 0.5 + 0.5;
+ float line = smoothstep(0.02, 0.0, d);
+ vec3 col = vec3(0.0);
+ col += vec3(0.0, 1.0, 0.8) * line * pulse;
+ col += vec3(1.0, 0.0, 0.5) * line * (1.0 - pulse);
+ col += vec3(0.02, 0.0, 0.04);
+ fragColor = vec4(col, 1.0);
+}""", ["grid", "neon", "synthwave", "retro", "tron", "pulse", "cyberpunk"], "2d", "Retro neon grid with alternating color pulses",
+{"chaos_level": 0.2, "color_temperature": "cool", "motion_type": "pulsing"})
+
+s("Ink in Water", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+float fbm(vec2 p) {
+ float v = 0.0, a = 0.5;
+ mat2 rot = mat2(0.8, 0.6, -0.6, 0.8);
+ for (int i = 0; i < 7; i++) { v += a * noise(p); p = rot * p * 2.0; a *= 0.5; }
+ return v;
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.15;
+ float f1 = fbm(uv * 3.0 + t);
+ float f2 = fbm(uv * 3.0 + f1 * 2.0 + t * 0.7);
+ float f3 = fbm(uv * 3.0 + f2 * 2.0);
+ vec3 col = mix(vec3(0.05, 0.02, 0.1), vec3(0.1, 0.0, 0.3), f1);
+ col = mix(col, vec3(0.6, 0.1, 0.2), f2 * 0.6);
+ col = mix(col, vec3(0.9, 0.8, 0.6), f3 * f3 * 0.5);
+ fragColor = vec4(col, 1.0);
+}""", ["fluid", "ink", "water", "organic", "fbm", "noise", "elegant", "dark"], "2d", "Layered fractal brownian motion simulating ink diffusing in water",
+{"chaos_level": 0.5, "color_temperature": "warm", "motion_type": "fluid"})
+
+s("Kaleidoscope Mirror", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float a = atan(uv.y, uv.x);
+ float r = length(uv);
+ float segments = 8.0;
+ a = mod(a, 6.2831 / segments);
+ a = abs(a - 3.14159 / segments);
+ vec2 p = vec2(cos(a), sin(a)) * r;
+ p += iTime * 0.2;
+ float pattern = sin(p.x * 15.0) * sin(p.y * 15.0);
+ pattern += sin(p.x * 7.0 + iTime) * cos(p.y * 9.0 - iTime * 0.5);
+ vec3 col = 0.5 + 0.5 * cos(pattern * 2.0 + iTime + vec3(0, 2, 4));
+ col *= smoothstep(1.5, 0.0, r);
+ fragColor = vec4(col, 1.0);
+}""", ["kaleidoscope", "symmetry", "mirror", "psychedelic", "colorful", "geometric"], "2d", "8-fold kaleidoscope with evolving sine patterns",
+{"chaos_level": 0.6, "color_temperature": "neutral", "motion_type": "rotating"})
+
+s("Plasma Lava Lamp", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.5;
+ float v = 0.0;
+ v += sin(uv.x * 10.0 + t);
+ v += sin((uv.y * 10.0 + t) * 0.5);
+ v += sin((uv.x * 10.0 + uv.y * 10.0 + t) * 0.3);
+ vec2 c = uv * 10.0 + vec2(sin(t * 0.3) * 5.0, cos(t * 0.5) * 5.0);
+ v += sin(length(c) + t);
+ v *= 0.5;
+ vec3 col;
+ col.r = sin(v * 3.14159) * 0.5 + 0.5;
+ col.g = sin(v * 3.14159 + 2.094) * 0.3 + 0.2;
+ col.b = sin(v * 3.14159 + 4.189) * 0.5 + 0.5;
+ fragColor = vec4(col, 1.0);
+}""", ["plasma", "lava", "retro", "colorful", "animated", "smooth", "classic"], "2d", "Classic plasma effect with lava lamp color cycling",
+{"chaos_level": 0.3, "color_temperature": "warm", "motion_type": "fluid"})
+
+s("Circuit Board", """
+float hash(float n) { return fract(sin(n) * 43758.5453); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec2 g = floor(uv * 30.0);
+ vec2 f = fract(uv * 30.0);
+ float id = hash(g.x + g.y * 137.0);
+ float trace = 0.0;
+ if (id > 0.5) trace = step(0.45, f.x) * step(f.x, 0.55);
+ else trace = step(0.45, f.y) * step(f.y, 0.55);
+ float node = smoothstep(0.3, 0.25, length(f - 0.5));
+ float pulse = sin(iTime * 3.0 + (g.x + g.y) * 0.5) * 0.5 + 0.5;
+ vec3 col = vec3(0.0, 0.15, 0.1);
+ col += vec3(0.0, 0.8, 0.4) * trace * (0.3 + 0.7 * pulse);
+ col += vec3(0.0, 1.0, 0.6) * node * pulse;
+ fragColor = vec4(col, 1.0);
+}""", ["circuit", "digital", "tech", "pcb", "green", "electronic", "grid"], "2d", "Procedural circuit board with animated signal pulses",
+{"chaos_level": 0.2, "color_temperature": "cool", "motion_type": "pulsing"})
+
+s("Aurora Borealis", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.2;
+ float n = 0.0;
+ n += noise(vec2(uv.x * 3.0 + t, uv.y * 0.5)) * 0.5;
+ n += noise(vec2(uv.x * 6.0 - t * 0.7, uv.y * 1.0 + t * 0.3)) * 0.25;
+ n += noise(vec2(uv.x * 12.0 + t * 0.5, uv.y * 2.0)) * 0.125;
+ float curtain = smoothstep(0.3, 0.7, uv.y) * smoothstep(1.0, 0.6, uv.y);
+ curtain *= n;
+ vec3 green = vec3(0.1, 0.9, 0.3);
+ vec3 purple = vec3(0.5, 0.1, 0.8);
+ vec3 col = mix(green, purple, uv.y) * curtain * 2.0;
+ col += vec3(0.01, 0.0, 0.03); // night sky
+ fragColor = vec4(col, 1.0);
+}""", ["aurora", "borealis", "nature", "night", "sky", "green", "purple", "atmospheric"], "2d", "Northern lights curtain with layered noise movement",
+{"chaos_level": 0.3, "color_temperature": "cool", "motion_type": "fluid"})
+
+s("Op Art Illusion", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float d = length(uv);
+ float a = atan(uv.y, uv.x);
+ float pattern = sin(d * 40.0 - iTime * 4.0) * sin(a * 12.0 + iTime * 2.0);
+ float bw = step(0.0, pattern);
+ float warp = sin(d * 20.0 + iTime) * 0.05;
+ bw = step(0.0, sin((d + warp) * 40.0 - iTime * 4.0) * sin(a * 12.0 + iTime * 2.0));
+ fragColor = vec4(vec3(bw), 1.0);
+}""", ["op-art", "illusion", "optical", "bw", "monochrome", "geometric", "hypnotic", "pattern"], "2d", "Black and white optical illusion with warping concentric rings",
+{"chaos_level": 0.7, "color_temperature": "monochrome", "motion_type": "pulsing"})
+
+s("Glitch Corruption", """
+float hash(float n) { return fract(sin(n) * 43758.5453); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = floor(iTime * 8.0);
+ float block = floor(uv.y * 20.0);
+ float shift = hash(block + t) * step(0.85, hash(block * 0.3 + t));
+ uv.x += shift * 0.3 * (hash(t + block * 7.0) - 0.5);
+ vec3 col;
+ col.r = step(0.5, fract(sin(dot(uv + 0.01, vec2(12.9, 78.2))) * 43758.5));
+ col.g = step(0.5, fract(sin(dot(uv, vec2(12.9, 78.2))) * 43758.5));
+ col.b = step(0.5, fract(sin(dot(uv - 0.01, vec2(12.9, 78.2))) * 43758.5));
+ float scanline = sin(uv.y * 500.0) * 0.04;
+ col += scanline;
+ fragColor = vec4(col, 1.0);
+}""", ["glitch", "corruption", "digital", "error", "rgb-split", "cyberpunk", "broken"], "2d", "Data corruption with RGB channel splitting and block displacement",
+{"chaos_level": 0.95, "color_temperature": "neutral", "motion_type": "chaotic"})
+
+s("Sunset Gradient Waves", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float wave = sin(uv.x * 8.0 + iTime) * 0.03;
+ wave += sin(uv.x * 15.0 - iTime * 1.5) * 0.015;
+ float y = uv.y + wave;
+ vec3 sky1 = vec3(0.1, 0.0, 0.2);
+ vec3 sky2 = vec3(0.8, 0.2, 0.1);
+ vec3 sky3 = vec3(1.0, 0.7, 0.2);
+ vec3 col;
+ if (y > 0.6) col = mix(sky2, sky1, (y - 0.6) / 0.4);
+ else if (y > 0.3) col = mix(sky3, sky2, (y - 0.3) / 0.3);
+ else col = mix(vec3(1.0, 0.9, 0.5), sky3, y / 0.3);
+ float sun = smoothstep(0.12, 0.1, length(vec2(uv.x - 0.5, y - 0.45)));
+ col = mix(col, vec3(1.0, 0.95, 0.8), sun);
+ fragColor = vec4(col, 1.0);
+}""", ["sunset", "gradient", "sky", "warm", "peaceful", "nature", "waves", "orange"], "2d", "Stylized sunset with layered gradient and gentle wave distortion",
+{"chaos_level": 0.1, "color_temperature": "warm", "motion_type": "fluid"})
+
+s("Hyperbolic Tiling", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime * 0.3;
+ float scale = 1.0;
+ vec3 col = vec3(0.0);
+ for (int i = 0; i < 8; i++) {
+ uv = abs(uv) - 0.5;
+ uv *= 1.5;
+ scale *= 1.5;
+ float fi = float(i);
+ uv *= mat2(cos(t + fi), sin(t + fi), -sin(t + fi), cos(t + fi));
+ }
+ float d = length(uv) / scale;
+ col = 0.5 + 0.5 * cos(d * 50.0 + iTime * 2.0 + vec3(0, 2, 4));
+ fragColor = vec4(col, 1.0);
+}""", ["hyperbolic", "tiling", "fractal", "recursive", "mathematical", "colorful", "abstract"], "2d", "Iterated function system creating hyperbolic-style tiling",
+{"chaos_level": 0.7, "color_temperature": "neutral", "motion_type": "rotating"})
+
+s("Liquid Metal", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.3;
+ vec2 p = uv * 4.0;
+ float n = noise(p + t) + noise(p * 2.0 - t * 0.7) * 0.5 + noise(p * 4.0 + t * 0.3) * 0.25;
+ n = n / 1.75;
+ float env = pow(n, 1.5);
+ vec3 col = mix(vec3(0.3, 0.3, 0.35), vec3(0.9, 0.9, 0.95), env);
+ float spec = pow(max(0.0, n * 2.0 - 1.0), 8.0);
+ col += vec3(1.0) * spec * 0.5;
+ col = mix(col, col * vec3(0.8, 0.85, 1.0), 0.3);
+ fragColor = vec4(col, 1.0);
+}""", ["metal", "liquid", "chrome", "silver", "reflective", "smooth", "elegant"], "2d", "Flowing liquid mercury surface with specular highlights",
+{"chaos_level": 0.3, "color_temperature": "cool", "motion_type": "fluid"})
+
+s("Firework Burst", """
+float hash(float n) { return fract(sin(n) * 43758.5453); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 col = vec3(0.0);
+ for (int b = 0; b < 5; b++) {
+ float fb = float(b);
+ float bt = mod(iTime + fb * 1.3, 4.0);
+ vec2 center = vec2(hash(fb * 7.0) - 0.5, hash(fb * 13.0) * 0.3 + 0.1) * 0.8;
+ float expand = bt * 0.4;
+ float fade = exp(-bt * 1.5);
+ for (int i = 0; i < 30; i++) {
+ float fi = float(i);
+ float angle = fi * 0.2094 + fb;
+ float speed = 0.8 + hash(fi + fb * 100.0) * 0.4;
+ vec2 pp = center + vec2(cos(angle), sin(angle)) * expand * speed;
+ pp.y -= bt * bt * 0.05; // gravity
+ float d = length(uv - pp);
+ vec3 sparkCol = 0.5 + 0.5 * cos(fb * 2.0 + vec3(0, 2, 4));
+ col += sparkCol * 0.003 / (d + 0.001) * fade;
+ }
+ }
+ col = min(col, 1.0);
+ fragColor = vec4(col, 1.0);
+}""", ["fireworks", "particles", "celebration", "explosion", "night", "colorful", "sparks"], "2d", "Multiple firework bursts with particle trails and gravity",
+{"chaos_level": 0.7, "color_temperature": "warm", "motion_type": "explosive"})
+
+s("Moiré Interference", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float d1 = length(uv - vec2(sin(iTime * 0.5) * 0.3, 0.0));
+ float d2 = length(uv + vec2(sin(iTime * 0.5) * 0.3, 0.0));
+ float p1 = sin(d1 * 60.0);
+ float p2 = sin(d2 * 60.0);
+ float moire = p1 * p2;
+ vec3 col = vec3(moire * 0.5 + 0.5);
+ fragColor = vec4(col, 1.0);
+}""", ["moire", "interference", "optical", "monochrome", "geometric", "minimal", "waves"], "2d", "Two overlapping circular wave patterns creating moiré interference",
+{"chaos_level": 0.4, "color_temperature": "monochrome", "motion_type": "oscillating"})
+
+s("Electric Storm", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime;
+ float bolt = 0.0;
+ for (int i = 0; i < 5; i++) {
+ float fi = float(i);
+ vec2 p = vec2(uv.x + noise(vec2(uv.y * 10.0 + t * 5.0, fi)) * 0.3, uv.y);
+ float d = abs(p.x - (fi - 2.0) * 0.15);
+ bolt += 0.005 / (d + 0.005) * step(0.7, noise(vec2(fi, floor(t * 3.0))));
+ }
+ vec3 col = vec3(0.02, 0.0, 0.05);
+ col += vec3(0.3, 0.5, 1.0) * bolt;
+ col += vec3(0.8, 0.9, 1.0) * bolt * bolt;
+ fragColor = vec4(col, 1.0);
+}""", ["lightning", "storm", "electric", "blue", "energy", "dramatic", "weather"], "2d", "Electric lightning bolts crackling across a dark sky",
+{"chaos_level": 0.8, "color_temperature": "cool", "motion_type": "chaotic"})
+
+s("Hexagonal Grid", """
+vec2 hexUV(vec2 uv) {
+ vec2 s = vec2(1.7320508, 1.0);
+ vec2 a = mod(uv, s) - s * 0.5;
+ vec2 b = mod(uv - s * 0.5, s) - s * 0.5;
+ return dot(a, a) < dot(b, b) ? a : b;
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec2 h = hexUV(uv * 8.0);
+ float d = length(h);
+ float edge = smoothstep(0.5, 0.48, d);
+ float inner = smoothstep(0.3, 0.28, d);
+ float pulse = sin(iTime * 2.0 + length(floor(uv * 8.0)) * 2.0) * 0.5 + 0.5;
+ vec3 col = vec3(0.05);
+ col += vec3(0.0, 0.6, 0.8) * (edge - inner) * 0.5;
+ col += vec3(0.0, 0.8, 1.0) * inner * pulse;
+ fragColor = vec4(col, 1.0);
+}""", ["hexagon", "grid", "geometric", "teal", "tech", "clean", "pattern", "honeycomb"], "2d", "Pulsing hexagonal grid with teal neon glow",
+{"chaos_level": 0.2, "color_temperature": "cool", "motion_type": "pulsing"})
+
+# ═══════════════════════════════════════════════════════════
+# 3D — RAY MARCHING
+# ═══════════════════════════════════════════════════════════
+
+s("Infinite Tunnel", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float a = atan(uv.y, uv.x);
+ float r = length(uv);
+ float tunnel = 1.0 / r;
+ float tex = sin(a * 6.0 + tunnel * 2.0 - iTime * 3.0);
+ tex *= sin(tunnel * 4.0 - iTime * 2.0);
+ vec3 col = 0.5 + 0.5 * cos(tex * 3.0 + iTime + vec3(0, 2, 4));
+ col *= smoothstep(0.0, 0.3, r) * (1.0 - smoothstep(0.8, 2.0, r));
+ col *= 1.0 / (r * 3.0);
+ fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
+}""", ["tunnel", "infinite", "warp", "psychedelic", "vortex", "3d-illusion", "trippy"], "3d", "Infinite tunnel flythrough with warping hexagonal texture",
+{"chaos_level": 0.6, "color_temperature": "neutral", "motion_type": "forward"})
+
+s("Raymarched Metaballs", """
+float sdSphere(vec3 p, float r) { return length(p) - r; }
+float map(vec3 p) {
+ float d = 1e10;
+ for (int i = 0; i < 5; i++) {
+ float fi = float(i);
+ vec3 center = vec3(
+ sin(iTime + fi * 1.3) * 0.8,
+ cos(iTime * 0.7 + fi * 2.1) * 0.6,
+ sin(iTime * 0.5 + fi * 0.9) * 0.5
+ );
+ d = min(d, sdSphere(p - center, 0.4));
+ }
+ // smooth union
+ return d;
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0.0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy), map(p+e.yxy)-map(p-e.yxy), map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, -3);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ for (int i = 0; i < 64; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001 || t > 20.0) break;
+ t += d;
+ }
+ vec3 col = vec3(0.02, 0.01, 0.05);
+ if (t < 20.0) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ vec3 light = normalize(vec3(1, 1, -1));
+ float diff = max(dot(n, light), 0.0);
+ float spec = pow(max(dot(reflect(-light, n), -rd), 0.0), 32.0);
+ col = vec3(0.8, 0.2, 0.4) * diff + vec3(1.0) * spec * 0.5 + vec3(0.05, 0.02, 0.08);
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["metaballs", "3d", "raymarching", "organic", "blobs", "smooth", "pink"], "3d", "Five orbiting raymarched spheres with specular lighting",
+{"chaos_level": 0.4, "color_temperature": "warm", "motion_type": "orbiting"})
+
+s("Fractal Mountain Terrain", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+float terrain(vec2 p) {
+ float h = 0.0, a = 0.5;
+ for (int i = 0; i < 6; i++) { h += a * noise(p); p *= 2.0; a *= 0.5; }
+ return h;
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(iTime * 0.5, 1.5, 0.0);
+ vec3 rd = normalize(vec3(uv.x, uv.y - 0.2, 1.0));
+ float t = 0.0;
+ vec3 col = vec3(0.4, 0.6, 0.9); // sky
+ for (int i = 0; i < 80; i++) {
+ vec3 p = ro + rd * t;
+ float h = terrain(p.xz) * 2.0;
+ if (p.y < h) {
+ vec3 n = normalize(vec3(
+ terrain(p.xz + vec2(0.01, 0)) - terrain(p.xz - vec2(0.01, 0)),
+ 0.02,
+ terrain(p.xz + vec2(0, 0.01)) - terrain(p.xz - vec2(0, 0.01))
+ ));
+ float sun = max(dot(n, normalize(vec3(1, 0.8, 0.5))), 0.0);
+ col = mix(vec3(0.2, 0.3, 0.1), vec3(0.5, 0.4, 0.3), p.y / 2.0) * (0.3 + 0.7 * sun);
+ col = mix(col, vec3(0.4, 0.6, 0.9), 1.0 - exp(-t * 0.03));
+ break;
+ }
+ t += 0.05 + t * 0.01;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["terrain", "mountain", "landscape", "3d", "raymarching", "nature", "flyover", "procedural"], "3d", "Procedural fractal terrain with aerial flyover and atmospheric fog",
+{"chaos_level": 0.3, "color_temperature": "neutral", "motion_type": "forward"})
+
+s("Neon Torus Knot", """
+float sdTorus(vec3 p, vec2 t) {
+ vec2 q = vec2(length(p.xz) - t.x, p.y);
+ return length(q) - t.y;
+}
+float map(vec3 p) {
+ float a = iTime * 0.5;
+ p.xz *= mat2(cos(a), sin(a), -sin(a), cos(a));
+ // trefoil knot via torus deformation
+ float r = length(p.xz);
+ float theta = atan(p.z, p.x);
+ vec3 q = vec3(r - 1.0, p.y, 0.0);
+ float knotAngle = theta * 3.0 + iTime;
+ q.xy -= 0.4 * vec2(cos(knotAngle), sin(knotAngle));
+ return length(q) - 0.15;
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy), map(p+e.yxy)-map(p-e.yxy), map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, -3.5);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ vec3 col = vec3(0.01);
+ for (int i = 0; i < 80; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ float fresnel = pow(1.0 - abs(dot(n, rd)), 3.0);
+ col = vec3(0.0, 0.8, 1.0) * (0.2 + 0.8 * fresnel);
+ col += vec3(1.0, 0.3, 0.8) * fresnel * fresnel;
+ float glow = 0.005 / (d + 0.005);
+ col += vec3(0.0, 0.4, 0.6) * glow;
+ break;
+ }
+ t += d;
+ if (t > 20.0) break;
+ // Accumulate glow from near-misses
+ col += vec3(0.0, 0.3, 0.5) * 0.002 / (d + 0.01);
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["torus", "knot", "3d", "neon", "raymarching", "glow", "cyan", "mathematical", "geometry"], "3d", "Raymarched trefoil torus knot with neon fresnel glow",
+{"chaos_level": 0.4, "color_temperature": "cool", "motion_type": "rotating"})
+
+s("Glass Refraction Orb", """
+float sdSphere(vec3 p, float r) { return length(p) - r; }
+float map(vec3 p) { return sdSphere(p, 1.0); }
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy), map(p+e.yxy)-map(p-e.yxy), map(p+e.yyx)-map(p-e.yyx)));
+}
+vec3 background(vec3 rd) {
+ float t = iTime * 0.3;
+ return 0.5 + 0.5 * cos(rd * 5.0 + t + vec3(0, 2, 4));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, -3);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ for (int i = 0; i < 64; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001) break;
+ t += d;
+ if (t > 20.0) break;
+ }
+ vec3 col;
+ if (t < 20.0) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ float fresnel = pow(1.0 - abs(dot(n, rd)), 4.0);
+ vec3 refl = reflect(rd, n);
+ vec3 refr = refract(rd, n, 0.7);
+ col = mix(background(refr) * 0.8, background(refl), fresnel);
+ col += vec3(1.0) * pow(max(dot(refl, normalize(vec3(1, 1, -1))), 0.0), 64.0) * 0.5;
+ } else {
+ col = background(rd) * 0.3;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["glass", "sphere", "refraction", "3d", "raymarching", "crystal", "transparent", "elegant"], "3d", "Transparent glass sphere with refraction and reflection",
+{"chaos_level": 0.2, "color_temperature": "neutral", "motion_type": "static"})
+
+# ═══════════════════════════════════════════════════════════
+# MORE 2D VARIETY
+# ═══════════════════════════════════════════════════════════
+
+s("Cellular Automata Flow", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = floor(iTime * 4.0);
+ vec2 cell = floor(uv * 40.0);
+ float alive = step(0.5, hash(cell + t * 0.01));
+ float neighbors = 0.0;
+ for (int x = -1; x <= 1; x++)
+ for (int y = -1; y <= 1; y++) {
+ if (x == 0 && y == 0) continue;
+ neighbors += step(0.5, hash(cell + vec2(float(x), float(y)) + t * 0.01));
+ }
+ float next = step(2.5, neighbors) * step(neighbors, 3.5) * (1.0 - alive)
+ + step(1.5, neighbors) * step(neighbors, 3.5) * alive;
+ vec3 col = mix(vec3(0.05, 0.0, 0.1), vec3(0.0, 0.8, 0.4), next);
+ fragColor = vec4(col, 1.0);
+}""", ["cellular-automata", "conway", "life", "grid", "simulation", "green", "digital"], "2d", "Game of Life style cellular automata with hash-based state",
+{"chaos_level": 0.6, "color_temperature": "cool", "motion_type": "evolving"})
+
+s("Blackhole Gravity Lens", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float a = atan(uv.y, uv.x);
+ float warp = 0.3 / (r + 0.01);
+ vec2 warped = vec2(cos(a + warp), sin(a + warp)) * (r + 0.1 / r);
+ float stars = step(0.98, fract(sin(dot(floor(warped * 20.0), vec2(127.1, 311.7))) * 43758.5453));
+ float disk = smoothstep(0.4, 0.3, abs(warped.y * 5.0 / (length(warped) + 0.1)))
+ * smoothstep(0.1, 0.2, r) * smoothstep(0.8, 0.4, r);
+ vec3 col = vec3(0.0);
+ col += vec3(0.7, 0.5, 0.3) * disk * (sin(warped.x * 30.0 + iTime * 5.0) * 0.3 + 0.7);
+ col += vec3(0.8, 0.9, 1.0) * stars * (1.0 - smoothstep(0.0, 0.15, r));
+ col += vec3(0.8, 0.9, 1.0) * stars * smoothstep(0.3, 0.5, r);
+ float shadow = smoothstep(0.12, 0.1, r);
+ col *= 1.0 - shadow;
+ fragColor = vec4(col, 1.0);
+}""", ["blackhole", "space", "gravity", "lens", "accretion", "cosmic", "sci-fi", "dark"], "2d", "Gravitational lensing around a black hole with accretion disk",
+{"chaos_level": 0.5, "color_temperature": "warm", "motion_type": "rotating"})
+
+s("Waveform Oscilloscope", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ uv.y = uv.y * 2.0 - 1.0;
+ float wave = sin(uv.x * 25.0 + iTime * 5.0) * 0.3;
+ wave += sin(uv.x * 13.0 - iTime * 3.0) * 0.15;
+ wave += sin(uv.x * 47.0 + iTime * 8.0) * 0.05;
+ float d = abs(uv.y - wave);
+ float line = 0.003 / d;
+ float glow = 0.01 / d;
+ vec3 col = vec3(0.0);
+ col += vec3(0.0, 1.0, 0.3) * line;
+ col += vec3(0.0, 0.4, 0.15) * glow;
+ float grid = step(0.99, fract(uv.x * 10.0)) + step(0.99, fract((uv.y * 0.5 + 0.5) * 5.0));
+ col += vec3(0.03) * grid;
+ fragColor = vec4(col, 1.0);
+}""", ["waveform", "oscilloscope", "audio", "signal", "green", "tech", "minimal", "retro"], "2d", "Animated oscilloscope display with composite waveform",
+{"chaos_level": 0.3, "color_temperature": "cool", "motion_type": "oscillating"})
+
+s("Sacred Geometry Flower", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float a = atan(uv.y, uv.x);
+ float petals = abs(sin(a * 6.0 + iTime * 0.5)) * 0.3 + 0.2;
+ float flower = smoothstep(petals + 0.01, petals, r);
+ float inner = smoothstep(0.08, 0.06, r);
+ float rings = sin(r * 60.0 - iTime * 3.0) * 0.5 + 0.5;
+ rings *= smoothstep(0.5, 0.1, r);
+ vec3 col = vec3(0.02, 0.0, 0.04);
+ col += vec3(0.8, 0.4, 0.1) * flower * rings;
+ col += vec3(1.0, 0.8, 0.3) * inner;
+ col += vec3(0.3, 0.0, 0.5) * (1.0 - flower) * rings * 0.3;
+ fragColor = vec4(col, 1.0);
+}""", ["sacred-geometry", "flower", "mandala", "spiritual", "gold", "symmetry", "organic"], "2d", "Sacred geometry flower of life with golden petal animation",
+{"chaos_level": 0.2, "color_temperature": "warm", "motion_type": "breathing"})
+
+s("Pixel Art Dither", """
+float dither(vec2 p, float v) {
+ mat4 bayer = mat4(
+ 0.0/16.0, 8.0/16.0, 2.0/16.0, 10.0/16.0,
+ 12.0/16.0, 4.0/16.0, 14.0/16.0, 6.0/16.0,
+ 3.0/16.0, 11.0/16.0, 1.0/16.0, 9.0/16.0,
+ 15.0/16.0, 7.0/16.0, 13.0/16.0, 5.0/16.0
+ );
+ int x = int(mod(p.x, 4.0));
+ int y = int(mod(p.y, 4.0));
+ float threshold = bayer[y][x];
+ return step(threshold, v);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec2 pixel = floor(fragCoord / 4.0);
+ float t = iTime * 0.5;
+ float wave = sin(uv.x * 5.0 + t) * sin(uv.y * 3.0 + t * 0.7) * 0.5 + 0.5;
+ float d = dither(pixel, wave);
+ vec3 dark = vec3(0.08, 0.04, 0.15);
+ vec3 light = vec3(0.3, 0.8, 0.6);
+ vec3 col = mix(dark, light, d);
+ fragColor = vec4(col, 1.0);
+}""", ["pixel-art", "dither", "retro", "8bit", "bayer", "low-fi", "nostalgic"], "2d", "Bayer matrix ordered dithering with animated value field",
+{"chaos_level": 0.3, "color_temperature": "cool", "motion_type": "pulsing"})
+
+s("Reaction Diffusion", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.1;
+ float n = 0.0;
+ float scale = 8.0;
+ n += noise(uv * scale + t);
+ n += noise(uv * scale * 2.0 + sin(t * 3.0)) * 0.5;
+ n += noise(uv * scale * 4.0 - t * 2.0) * 0.25;
+ n = sin(n * 8.0) * 0.5 + 0.5;
+ float spots = smoothstep(0.4, 0.6, n);
+ vec3 col = mix(vec3(0.9, 0.85, 0.7), vec3(0.15, 0.1, 0.05), spots);
+ fragColor = vec4(col, 1.0);
+}""", ["reaction-diffusion", "organic", "pattern", "turing", "biological", "spots", "nature"], "2d", "Turing-style reaction diffusion patterns like animal markings",
+{"chaos_level": 0.4, "color_temperature": "warm", "motion_type": "morphing"})
+
+s("Tron Light Cycle Trail", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec3 col = vec3(0.02, 0.02, 0.05);
+ // Perspective grid floor
+ float gy = 0.5 / (1.0 - uv.y + 0.001);
+ float gx = (uv.x - 0.5) * gy * 2.0;
+ float gridX = smoothstep(0.02, 0.0, abs(fract(gx + iTime * 0.5) - 0.5));
+ float gridZ = smoothstep(0.02, 0.0, abs(fract(gy * 0.5 - iTime * 2.0) - 0.5));
+ float grid = max(gridX, gridZ) * step(0.5, uv.y) * (1.0 - uv.y) * 4.0;
+ col += vec3(0.0, 0.5, 1.0) * grid * 0.5;
+ // Light trail
+ float trail = smoothstep(0.01, 0.0, abs(uv.x - 0.5 - sin(iTime) * 0.2));
+ trail *= step(0.5, uv.y);
+ col += vec3(0.0, 0.8, 1.0) * trail * 2.0;
+ fragColor = vec4(col, 1.0);
+}""", ["tron", "grid", "synthwave", "cyberpunk", "blue", "neon", "retro-future", "perspective"], "2d", "Tron-style perspective grid with light cycle trail",
+{"chaos_level": 0.2, "color_temperature": "cool", "motion_type": "forward"})
+
+s("Watercolor Bleed", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+float fbm(vec2 p) {
+ float v = 0.0, a = 0.5;
+ mat2 rot = mat2(0.8, 0.6, -0.6, 0.8);
+ for (int i = 0; i < 6; i++) { v += a * noise(p); p = rot * p * 2.0; a *= 0.5; }
+ return v;
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.05;
+ float f = fbm(uv * 4.0 + t);
+ vec3 col1 = vec3(0.9, 0.3, 0.2);
+ vec3 col2 = vec3(0.2, 0.5, 0.9);
+ vec3 col3 = vec3(0.1, 0.8, 0.4);
+ vec3 col = mix(col1, col2, smoothstep(0.3, 0.5, f));
+ col = mix(col, col3, smoothstep(0.5, 0.7, f));
+ col = mix(col, vec3(0.95, 0.92, 0.88), smoothstep(0.7, 0.9, f));
+ // paper texture
+ float paper = noise(uv * 200.0) * 0.05 + 0.95;
+ col *= paper;
+ fragColor = vec4(col, 1.0);
+}""", ["watercolor", "paint", "artistic", "soft", "pastel", "organic", "paper", "art"], "2d", "Watercolor paint bleeding on textured paper",
+{"chaos_level": 0.3, "color_temperature": "warm", "motion_type": "fluid"})
+
+# ═══════════════════════════════════════════════════════════
+# PROGRAMMATIC GENERATION — fill to 200+
+# We generate families of variations mathematically
+# ═══════════════════════════════════════════════════════════
+
+# --- Family: Sine field variations (20 shaders) ---
+sine_colors = [
+ ("Crimson Pulse", "vec3(0.9, 0.1, 0.15)", "vec3(0.3, 0.0, 0.05)", ["red", "pulse", "warm", "intense"]),
+ ("Ocean Depth", "vec3(0.0, 0.3, 0.9)", "vec3(0.0, 0.05, 0.15)", ["ocean", "blue", "deep", "calm"]),
+ ("Toxic Glow", "vec3(0.2, 1.0, 0.1)", "vec3(0.0, 0.1, 0.0)", ["toxic", "green", "radioactive", "glow"]),
+ ("Amber Warmth", "vec3(1.0, 0.6, 0.1)", "vec3(0.15, 0.05, 0.0)", ["amber", "warm", "golden", "honey"]),
+ ("Violet Dream", "vec3(0.6, 0.1, 0.9)", "vec3(0.1, 0.0, 0.15)", ["violet", "dream", "purple", "ethereal"]),
+ ("Ice Crystal", "vec3(0.7, 0.9, 1.0)", "vec3(0.05, 0.08, 0.12)", ["ice", "crystal", "frozen", "cold"]),
+ ("Coral Reef", "vec3(1.0, 0.4, 0.5)", "vec3(0.1, 0.02, 0.05)", ["coral", "reef", "pink", "organic"]),
+ ("Forest Canopy", "vec3(0.1, 0.6, 0.2)", "vec3(0.02, 0.08, 0.02)", ["forest", "green", "nature", "canopy"]),
+ ("Copper Wire", "vec3(0.9, 0.5, 0.2)", "vec3(0.1, 0.03, 0.0)", ["copper", "metal", "wire", "industrial"]),
+ ("Midnight Blue", "vec3(0.1, 0.15, 0.5)", "vec3(0.01, 0.01, 0.05)", ["midnight", "blue", "dark", "night"]),
+ ("Sunrise Ember", "vec3(1.0, 0.3, 0.0)", "vec3(0.2, 0.02, 0.0)", ["sunrise", "ember", "fire", "dawn"]),
+ ("Lavender Haze", "vec3(0.7, 0.5, 0.9)", "vec3(0.08, 0.05, 0.12)", ["lavender", "haze", "soft", "pastel"]),
+ ("Steel Grey", "vec3(0.5, 0.52, 0.55)", "vec3(0.05, 0.05, 0.06)", ["steel", "grey", "industrial", "monochrome"]),
+ ("Magma Core", "vec3(1.0, 0.2, 0.0)", "vec3(0.3, 0.0, 0.0)", ["magma", "lava", "volcanic", "hot"]),
+ ("Teal Abyss", "vec3(0.0, 0.7, 0.6)", "vec3(0.0, 0.08, 0.07)", ["teal", "abyss", "aqua", "deep"]),
+ ("Champagne", "vec3(0.95, 0.85, 0.6)", "vec3(0.12, 0.1, 0.05)", ["champagne", "gold", "luxury", "elegant"]),
+ ("Electric Lime", "vec3(0.6, 1.0, 0.0)", "vec3(0.05, 0.12, 0.0)", ["electric", "lime", "acid", "bright"]),
+ ("Blood Moon", "vec3(0.6, 0.05, 0.05)", "vec3(0.1, 0.0, 0.02)", ["blood", "moon", "dark-red", "ominous"]),
+ ("Arctic Wind", "vec3(0.85, 0.95, 1.0)", "vec3(0.08, 0.1, 0.15)", ["arctic", "wind", "white", "cold"]),
+ ("Rust Decay", "vec3(0.6, 0.3, 0.1)", "vec3(0.08, 0.03, 0.01)", ["rust", "decay", "brown", "aged"]),
+]
+
+for i, (name, bright, dark, extra_tags) in enumerate(sine_colors):
+ freq_x = 5 + (i % 7) * 3
+ freq_y = 7 + (i % 5) * 4
+ speed = 0.5 + (i % 4) * 0.3
+ complexity = ["sin", "cos", "sin+cos"][i % 3]
+
+ code = f"""void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime * {speed:.1f};
+ float v = sin(uv.x * {freq_x:.1f} + t) * cos(uv.y * {freq_y:.1f} - t * 0.7);
+ v += sin(length(uv) * {freq_x + freq_y:.1f} + t * 1.3) * 0.5;
+ v = v * 0.5 + 0.5;
+ v = pow(v, 1.5);
+ vec3 col = mix({dark}, {bright}, v);
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["sine-field", "mathematical", "animated"] + extra_tags,
+ "2d", f"Sine wave interference pattern in {name.lower()} palette",
+ {"chaos_level": 0.3 + (i % 5) * 0.1, "color_temperature": ["warm", "cool", "neutral"][i % 3], "motion_type": "pulsing"})
+
+# --- Family: Rotating geometry (15 shaders) ---
+geometries = [
+ ("Spinning Cube Wireframe", 4, "vec3(0.0, 0.8, 1.0)", ["cube", "wireframe", "3d-illusion", "blue"]),
+ ("Pentagonal Vortex", 5, "vec3(0.9, 0.3, 0.8)", ["pentagon", "vortex", "pink"]),
+ ("Triangular Recursion", 3, "vec3(1.0, 0.8, 0.0)", ["triangle", "recursive", "yellow"]),
+ ("Octagonal Mandala", 8, "vec3(0.3, 0.9, 0.5)", ["octagon", "mandala", "green"]),
+ ("Star Field Rotation", 5, "vec3(0.9, 0.9, 1.0)", ["star", "rotation", "white"]),
+ ("Decagonal Wave", 10, "vec3(0.8, 0.4, 0.0)", ["decagon", "wave", "orange"]),
+ ("Heptagonal Bloom", 7, "vec3(0.5, 0.0, 0.9)", ["heptagon", "bloom", "purple"]),
+ ("Square Spiral", 4, "vec3(1.0, 0.2, 0.3)", ["square", "spiral", "red"]),
+ ("Hexagonal Pulse", 6, "vec3(0.0, 0.9, 0.7)", ["hexagon", "pulse", "teal"]),
+ ("Nonagonal Flow", 9, "vec3(0.9, 0.7, 0.3)", ["nonagon", "flow", "gold"]),
+ ("Diamond Lattice", 4, "vec3(0.6, 0.8, 1.0)", ["diamond", "lattice", "crystal"]),
+ ("Trigon Collapse", 3, "vec3(1.0, 0.0, 0.5)", ["triangle", "collapse", "magenta"]),
+ ("Polygon Storm", 12, "vec3(0.4, 0.4, 0.9)", ["polygon", "storm", "blue"]),
+ ("Angular Meditation", 6, "vec3(0.9, 0.85, 0.7)", ["angular", "meditation", "calm"]),
+ ("Vertex Dance", 8, "vec3(0.0, 1.0, 0.4)", ["vertex", "dance", "neon-green"]),
+]
+
+for i, (name, sides, color, extra_tags) in enumerate(geometries):
+ speed = 0.5 + (i % 3) * 0.3
+ layers = 3 + i % 4
+
+ code = f"""void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime * {speed:.1f};
+ vec3 col = vec3(0.02);
+ for (int layer = 0; layer < {layers}; layer++) {{
+ float fl = float(layer);
+ float scale = 1.0 + fl * 0.5;
+ float rot = t + fl * 0.5;
+ vec2 p = uv * scale;
+ p = vec2(p.x * cos(rot) - p.y * sin(rot), p.x * sin(rot) + p.y * cos(rot));
+ float a = atan(p.y, p.x);
+ float r = length(p);
+ float sides = {float(sides):.1f};
+ float polygon = cos(3.14159 / sides) / cos(mod(a + 3.14159 / sides, 2.0 * 3.14159 / sides) - 3.14159 / sides);
+ float edge = abs(r - polygon * (0.3 + fl * 0.1));
+ float line = 0.003 / (edge + 0.003);
+ col += {color} * line * (0.5 + 0.5 / (1.0 + fl));
+ }}
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["geometric", "rotating", "polygon", "animated", "line-art"] + extra_tags,
+ "2d", f"Layered rotating {sides}-sided polygon wireframes",
+ {"chaos_level": 0.3 + (i % 4) * 0.1, "color_temperature": ["cool", "warm", "neutral"][i % 3], "motion_type": "rotating"})
+
+# --- Family: Noise landscape variations (15 shaders) ---
+landscapes = [
+ ("Desert Dunes", "vec3(0.9, 0.7, 0.4)", "vec3(0.4, 0.6, 0.9)", 0.3, ["desert", "dunes", "sand", "warm"]),
+ ("Frozen Tundra", "vec3(0.85, 0.9, 0.95)", "vec3(0.3, 0.4, 0.5)", 0.15, ["frozen", "tundra", "ice", "cold"]),
+ ("Volcanic Plains", "vec3(0.3, 0.1, 0.05)", "vec3(0.1, 0.0, 0.0)", 0.8, ["volcanic", "lava", "dark", "ominous"]),
+ ("Rolling Hills", "vec3(0.2, 0.5, 0.15)", "vec3(0.5, 0.7, 0.9)", 0.4, ["hills", "green", "pastoral", "peaceful"]),
+ ("Alien Terrain", "vec3(0.4, 0.0, 0.5)", "vec3(0.1, 0.8, 0.3)", 0.6, ["alien", "sci-fi", "surreal", "otherworldly"]),
+ ("Oceanic Floor", "vec3(0.05, 0.15, 0.25)", "vec3(0.0, 0.3, 0.5)", 0.35, ["ocean-floor", "underwater", "deep", "blue"]),
+ ("Mars Surface", "vec3(0.7, 0.3, 0.1)", "vec3(0.3, 0.15, 0.1)", 0.5, ["mars", "planet", "red", "space"]),
+ ("Crystal Cavern", "vec3(0.3, 0.5, 0.7)", "vec3(0.1, 0.0, 0.2)", 0.55, ["crystal", "cavern", "underground", "sparkle"]),
+ ("Cloud Tops", "vec3(0.95, 0.95, 1.0)", "vec3(0.3, 0.5, 0.8)", 0.2, ["clouds", "sky", "white", "fluffy"]),
+ ("Moss Garden", "vec3(0.15, 0.35, 0.1)", "vec3(0.3, 0.4, 0.3)", 0.25, ["moss", "garden", "zen", "green"]),
+ ("Rust Belt", "vec3(0.5, 0.25, 0.1)", "vec3(0.2, 0.15, 0.1)", 0.45, ["rust", "industrial", "decay", "brown"]),
+ ("Nebula Gas", "vec3(0.6, 0.2, 0.8)", "vec3(0.05, 0.0, 0.1)", 0.7, ["nebula", "gas", "space", "purple"]),
+ ("Coral Depths", "vec3(0.9, 0.4, 0.3)", "vec3(0.0, 0.1, 0.2)", 0.4, ["coral", "ocean", "underwater", "warm"]),
+ ("Storm Front", "vec3(0.2, 0.22, 0.25)", "vec3(0.05, 0.05, 0.08)", 0.65, ["storm", "dark", "weather", "dramatic"]),
+ ("Golden Hour", "vec3(1.0, 0.75, 0.4)", "vec3(0.3, 0.2, 0.4)", 0.3, ["golden-hour", "warm", "photography", "sunset"]),
+]
+
+for i, (name, ground, sky, roughness, extra_tags) in enumerate(landscapes):
+ octaves = 5 + i % 3
+ speed = 0.1 + (i % 4) * 0.1
+ code = f"""float hash(vec2 p) {{ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }}
+float noise(vec2 p) {{
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * {speed:.2f};
+ vec2 p = uv * 5.0 + vec2(t, 0.0);
+ float h = 0.0, a = 0.5;
+ for (int i = 0; i < {octaves}; i++) {{ h += a * noise(p); p *= 2.0; a *= 0.5; }}
+ float terrain = smoothstep(0.3, 0.7, h + (uv.y - 0.5) * {roughness + 0.5:.1f});
+ vec3 col = mix({ground}, {sky}, terrain);
+ float fog = smoothstep(0.0, 1.0, uv.y);
+ col = mix(col, {sky} * 0.7, fog * 0.3);
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["landscape", "terrain", "noise", "procedural"] + extra_tags,
+ "2d", f"Procedural {name.lower()} landscape with {octaves}-octave noise",
+ {"chaos_level": roughness, "color_temperature": ["warm", "cool", "neutral"][i % 3], "motion_type": "scrolling"})
+
+# --- Family: 3D raymarched primitives (15 shaders) ---
+rm_shapes = [
+ ("Warped Cube", "max(max(abs(p.x),abs(p.y)),abs(p.z))-0.8+sin(p.x*5.0+iTime)*0.1", "vec3(0.9,0.3,0.1)", ["cube", "warped", "organic"]),
+ ("Spinning Top", "length(vec2(length(p.xz)-0.6,p.y))-0.2", "vec3(0.1,0.7,0.9)", ["torus", "spinning", "blue"]),
+ ("Infinite Pillars", "length(mod(p.xz+1.0,2.0)-1.0)-0.3", "vec3(0.8,0.8,0.85)", ["pillars", "infinite", "architectural"]),
+ ("Twisted Column", "length(vec2(length(p.xz*mat2(cos(p.y),sin(p.y),-sin(p.y),cos(p.y)))-0.5,0.0))-0.15", "vec3(0.6,0.2,0.8)", ["twisted", "column", "purple"]),
+ ("Gyroid Surface", "sin(p.x)*cos(p.y)+sin(p.y)*cos(p.z)+sin(p.z)*cos(p.x)", "vec3(0.2,0.8,0.4)", ["gyroid", "minimal-surface", "mathematical"]),
+ ("Sierpinski Tetra", "max(max(-p.x-p.y-p.z,p.x+p.y-p.z),max(-p.x+p.y+p.z,p.x-p.y+p.z))-1.0", "vec3(1.0,0.8,0.2)", ["sierpinski", "fractal", "gold"]),
+ ("Rounded Box", "length(max(abs(p)-vec3(0.6,0.4,0.3),0.0))-0.1", "vec3(0.3,0.5,0.9)", ["box", "rounded", "smooth"]),
+ ("Egg Shape", "length(p*vec3(1.0,1.3,1.0))-0.8", "vec3(0.95,0.9,0.8)", ["egg", "organic", "smooth", "minimal"]),
+ ("Capped Cylinder", "max(length(p.xz)-0.4,abs(p.y)-1.0)", "vec3(0.7,0.3,0.3)", ["cylinder", "geometric", "red"]),
+ ("Octahedron", "(abs(p.x)+abs(p.y)+abs(p.z)-1.0)*0.577", "vec3(0.4,0.9,0.7)", ["octahedron", "platonic", "teal"]),
+ ("Capsule Link", "length(p-vec3(0,clamp(p.y,-0.5,0.5),0))-0.3", "vec3(0.9,0.5,0.2)", ["capsule", "simple", "orange"]),
+ ("Cross Shape", "min(min(length(p.xy)-0.2,length(p.yz)-0.2),length(p.xz)-0.2)", "vec3(0.8,0.1,0.1)", ["cross", "intersection", "red"]),
+ ("Pulsing Heart", "pow(p.x*p.x+0.9*p.y*p.y+p.z*p.z-1.0,3.0)-p.x*p.x*p.y*p.y*p.y-0.1*p.z*p.z*p.y*p.y*p.y", "vec3(0.9,0.15,0.2)", ["heart", "love", "romantic"]),
+ ("Klein Bottle", "length(vec2(length(p.xy)-1.0,p.z))-0.3", "vec3(0.5,0.7,1.0)", ["klein-bottle", "topology", "mathematical"]),
+ ("Menger Sponge", "max(max(abs(p.x),abs(p.y)),abs(p.z))-1.0", "vec3(0.6,0.6,0.65)", ["menger", "sponge", "fractal", "recursive"]),
+]
+
+for i, (name, sdf, color, extra_tags) in enumerate(rm_shapes):
+ rot_speed = 0.3 + (i % 4) * 0.2
+ code = f"""float map(vec3 p) {{
+ float a = iTime * {rot_speed:.1f};
+ p.xz *= mat2(cos(a), sin(a), -sin(a), cos(a));
+ p.xy *= mat2(cos(a*0.7), sin(a*0.7), -sin(a*0.7), cos(a*0.7));
+ return {sdf};
+}}
+vec3 getNormal(vec3 p) {{
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy),map(p+e.yxy)-map(p-e.yxy),map(p+e.yyx)-map(p-e.yyx)));
+}}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, -3.5);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ vec3 col = vec3(0.02, 0.01, 0.04);
+ for (int i = 0; i < 80; i++) {{
+ float d = map(ro + rd * t);
+ if (abs(d) < 0.001) {{
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ vec3 light = normalize(vec3(1, 1, -1));
+ float diff = max(dot(n, light), 0.0);
+ float spec = pow(max(dot(reflect(-light, n), -rd), 0.0), 16.0);
+ col = {color} * (0.15 + 0.85 * diff) + vec3(1.0) * spec * 0.3;
+ break;
+ }}
+ t += abs(d) * 0.8;
+ if (t > 20.0) break;
+ }}
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["3d", "raymarching", "sdf", "animated"] + extra_tags,
+ "3d", f"Raymarched {name.lower()} with dual-axis rotation and specular lighting",
+ {"chaos_level": 0.3 + (i % 5) * 0.1, "color_temperature": ["cool", "warm", "neutral"][i % 3], "motion_type": "rotating"})
+
+# --- Family: Abstract energy fields (15 shaders) ---
+energies = [
+ ("Plasma Core", 8.0, 3.0, "vec3(1.0,0.3,0.1)", "vec3(0.3,0.1,0.8)", ["plasma", "core", "energy"]),
+ ("Neural Network", 15.0, 5.0, "vec3(0.0,0.8,0.9)", "vec3(0.0,0.2,0.3)", ["neural", "network", "connections"]),
+ ("Magnetic Field", 6.0, 2.0, "vec3(0.2,0.5,1.0)", "vec3(0.8,0.2,0.1)", ["magnetic", "field", "physics"]),
+ ("Solar Flare", 4.0, 6.0, "vec3(1.0,0.7,0.0)", "vec3(1.0,0.1,0.0)", ["solar", "flare", "sun"]),
+ ("Quantum Foam", 20.0, 8.0, "vec3(0.5,0.8,1.0)", "vec3(0.1,0.0,0.2)", ["quantum", "foam", "microscopic"]),
+ ("Nebula Birth", 3.0, 1.5, "vec3(0.8,0.3,0.9)", "vec3(0.1,0.5,0.8)", ["nebula", "birth", "cosmic"]),
+ ("Lava Flow", 5.0, 2.0, "vec3(1.0,0.4,0.0)", "vec3(0.3,0.0,0.0)", ["lava", "flow", "volcanic"]),
+ ("Energy Shield", 10.0, 4.0, "vec3(0.0,0.9,0.5)", "vec3(0.0,0.2,0.1)", ["shield", "force-field", "sci-fi"]),
+ ("Radioactive Decay", 12.0, 7.0, "vec3(0.3,1.0,0.0)", "vec3(0.0,0.15,0.0)", ["radioactive", "decay", "toxic"]),
+ ("Dark Matter", 7.0, 3.0, "vec3(0.15,0.1,0.2)", "vec3(0.4,0.3,0.5)", ["dark-matter", "mysterious", "space"]),
+ ("Bioluminescent", 9.0, 4.0, "vec3(0.0,0.7,0.9)", "vec3(0.0,0.3,0.1)", ["bioluminescent", "ocean", "glow"]),
+ ("Cosmic Web", 6.0, 5.0, "vec3(0.6,0.7,1.0)", "vec3(0.05,0.0,0.1)", ["cosmic", "web", "universe"]),
+ ("Aurora Pulse", 8.0, 3.0, "vec3(0.1,0.9,0.4)", "vec3(0.4,0.1,0.8)", ["aurora", "pulse", "atmospheric"]),
+ ("Plasma Tornado", 5.0, 6.0, "vec3(0.9,0.5,1.0)", "vec3(0.2,0.0,0.3)", ["tornado", "vortex", "spinning"]),
+ ("Star Forge", 4.0, 4.0, "vec3(1.0,0.9,0.5)", "vec3(0.5,0.1,0.0)", ["star", "forge", "creation"]),
+]
+
+for i, (name, freq, speed, col1, col2, extra_tags) in enumerate(energies):
+ code = f"""float hash(vec2 p) {{ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }}
+float noise(vec2 p) {{
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime * {speed / 3.0:.2f};
+ float r = length(uv);
+ float a = atan(uv.y, uv.x);
+ float n = noise(vec2(a * {freq:.1f} / 6.283, r * {freq:.1f}) + t);
+ n += noise(vec2(a * {freq * 2:.1f} / 6.283, r * {freq * 2:.1f}) - t * 1.3) * 0.5;
+ n = n / 1.5;
+ float energy = pow(n, 2.0) * exp(-r * 2.0);
+ vec3 col = mix({col2}, {col1}, energy * 2.0);
+ col += {col1} * 0.01 / (r + 0.01) * n;
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["energy", "abstract", "animated", "glow"] + extra_tags,
+ "2d", f"Swirling energy field: {name.lower()}",
+ {"chaos_level": 0.4 + (i % 5) * 0.1, "color_temperature": ["warm", "cool", "neutral"][i % 3], "motion_type": "swirling"})
+
+# --- Family: Fractal Explorations (15 shaders) ---
+fractals = [
+ ("Julia Set Amethyst", -0.7269, 0.1889, "vec3(0.5,0.2,0.8)", ["julia", "amethyst", "fractal"]),
+ ("Julia Set Coral", -0.8, 0.156, "vec3(0.9,0.4,0.3)", ["julia", "coral", "fractal"]),
+ ("Julia Set Ice", -0.4, 0.6, "vec3(0.5,0.8,1.0)", ["julia", "ice", "fractal"]),
+ ("Julia Set Fire", -0.12, 0.74, "vec3(1.0,0.4,0.0)", ["julia", "fire", "fractal"]),
+ ("Julia Set Forest", 0.285, 0.01, "vec3(0.2,0.7,0.3)", ["julia", "forest", "fractal"]),
+ ("Julia Set Electric", -0.74543, 0.11301, "vec3(0.0,0.6,1.0)", ["julia", "electric", "fractal"]),
+ ("Julia Set Midnight", -0.75, 0.11, "vec3(0.15,0.1,0.3)", ["julia", "midnight", "fractal"]),
+ ("Julia Set Gold", -0.1, 0.65, "vec3(0.9,0.7,0.2)", ["julia", "gold", "fractal"]),
+ ("Julia Set Rose", -0.7, 0.27015, "vec3(0.9,0.3,0.5)", ["julia", "rose", "fractal"]),
+ ("Julia Set Nebula", 0.355, 0.355, "vec3(0.6,0.3,0.9)", ["julia", "nebula", "fractal"]),
+ ("Julia Set Ocean", -0.75, 0.0, "vec3(0.1,0.5,0.8)", ["julia", "ocean", "fractal"]),
+ ("Julia Set Ember", -0.77, 0.22, "vec3(0.8,0.2,0.05)", ["julia", "ember", "fractal"]),
+ ("Julia Set Toxic", -0.8, 0.0, "vec3(0.3,0.9,0.1)", ["julia", "toxic", "fractal"]),
+ ("Julia Set Void", 0.0, 0.65, "vec3(0.3,0.3,0.35)", ["julia", "void", "fractal"]),
+ ("Julia Set Prism", -0.7, 0.3, "vec3(0.7,0.5,1.0)", ["julia", "prism", "fractal"]),
+]
+
+for i, (name, cx, cy, color, extra_tags) in enumerate(fractals):
+ code = f"""void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ uv *= 2.5;
+ vec2 c = vec2({cx}, {cy}) + vec2(sin(iTime * 0.1) * 0.02, cos(iTime * 0.13) * 0.02);
+ vec2 z = uv;
+ int iter = 0;
+ for (int i = 0; i < 200; i++) {{
+ z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c;
+ if (dot(z, z) > 4.0) break;
+ iter = i;
+ }}
+ float t = float(iter) / 200.0;
+ vec3 col = {color} * (1.0 - pow(t, 0.5));
+ col += 0.5 + 0.5 * cos(t * 12.0 + iTime + vec3(0, 2, 4)) * (1.0 - t);
+ col *= 1.0 - step(199.0, float(iter));
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["fractal", "julia-set", "mathematical", "complex", "colorful"] + extra_tags,
+ "2d", f"Julia set with c=({cx}, {cy}), slowly drifting",
+ {"chaos_level": 0.5 + (i % 4) * 0.1, "color_temperature": ["warm", "cool", "neutral"][i % 3], "motion_type": "morphing"})
+
+# --- Family: Pattern/texture generators (15 shaders) ---
+patterns = [
+ ("Checkerboard Warp", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float warp = sin(length(uv) * 10.0 - iTime * 2.0) * 0.1;
+ uv += uv * warp;
+ vec2 grid = floor(uv * 8.0);
+ float check = mod(grid.x + grid.y, 2.0);
+ vec3 col = mix(vec3(0.1), vec3(0.9), check);
+ fragColor = vec4(col, 1.0);
+}""", ["checkerboard", "warp", "distortion", "bw", "optical"]),
+ ("Concentric Rings", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float rings = sin(r * 30.0 - iTime * 3.0) * 0.5 + 0.5;
+ vec3 col = mix(vec3(0.05, 0.0, 0.1), vec3(0.8, 0.3, 0.9), rings);
+ col *= smoothstep(1.5, 0.0, r);
+ fragColor = vec4(col, 1.0);
+}""", ["concentric", "rings", "purple", "hypnotic", "simple"]),
+ ("Herringbone", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec2 g = uv * vec2(20.0, 40.0);
+ float row = floor(g.y);
+ g.x += mod(row, 2.0) * 0.5;
+ vec2 f = fract(g);
+ float brick = step(0.05, f.x) * step(0.05, f.y);
+ float t = sin(iTime + row * 0.3) * 0.5 + 0.5;
+ vec3 col = mix(vec3(0.6, 0.3, 0.15), vec3(0.7, 0.4, 0.2), t) * brick;
+ col += vec3(0.3, 0.15, 0.05) * (1.0 - brick);
+ fragColor = vec4(col, 1.0);
+}""", ["herringbone", "brick", "pattern", "architecture", "warm"]),
+ ("Dots Matrix", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec2 grid = fract(uv * 20.0) - 0.5;
+ vec2 id = floor(uv * 20.0);
+ float phase = sin(iTime * 2.0 + id.x * 0.5 + id.y * 0.7);
+ float radius = 0.2 + 0.15 * phase;
+ float d = smoothstep(radius, radius - 0.02, length(grid));
+ vec3 col = vec3(0.0, 0.5, 0.8) * d;
+ fragColor = vec4(col, 1.0);
+}""", ["dots", "matrix", "halftone", "blue", "pulsing"]),
+ ("Weave Pattern", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec2 g = uv * 15.0;
+ float weft = step(0.3, fract(g.x)) * step(fract(g.x), 0.7);
+ float warp = step(0.3, fract(g.y)) * step(fract(g.y), 0.7);
+ float over = step(0.5, fract(floor(g.x) * 0.5 + floor(g.y) * 0.5));
+ float thread = mix(weft, warp, over);
+ vec3 col1 = vec3(0.2, 0.3, 0.6);
+ vec3 col2 = vec3(0.7, 0.5, 0.2);
+ vec3 col = mix(col1, col2, thread);
+ col *= 0.7 + 0.3 * sin(iTime + uv.x * 10.0);
+ fragColor = vec4(col, 1.0);
+}""", ["weave", "textile", "fabric", "pattern", "craft"]),
+ ("Sierpinski Triangle", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ uv.y = 1.0 - uv.y;
+ float t = mod(iTime * 0.3, 1.0);
+ float zoom = pow(2.0, t * 3.0);
+ uv = uv * zoom - vec2(zoom * 0.5 - 0.5);
+ float v = 1.0;
+ for (int i = 0; i < 20; i++) {
+ if (uv.x + uv.y > 1.0) { v = 0.0; break; }
+ uv *= 2.0;
+ if (uv.x > 1.0) uv.x -= 1.0;
+ if (uv.y > 1.0) uv.y -= 1.0;
+ }
+ vec3 col = vec3(v) * vec3(0.3, 0.6, 0.9);
+ fragColor = vec4(col, 1.0);
+}""", ["sierpinski", "triangle", "fractal", "self-similar", "zoom"]),
+ ("Perlin Contours", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float n = noise(uv * 6.0 + iTime * 0.2) * 0.5
+ + noise(uv * 12.0 - iTime * 0.15) * 0.25
+ + noise(uv * 24.0 + iTime * 0.1) * 0.125;
+ float contour = abs(fract(n * 10.0) - 0.5) * 2.0;
+ contour = smoothstep(0.0, 0.1, contour);
+ vec3 col = mix(vec3(0.9, 0.85, 0.75), vec3(0.2, 0.15, 0.1), 1.0 - contour);
+ fragColor = vec4(col, 1.0);
+}""", ["contour", "topographic", "map", "noise", "minimal", "clean"]),
+ ("Stained Glass", """
+vec2 hash2(vec2 p) {
+ p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
+ return fract(sin(p) * 43758.5453);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.y;
+ vec2 n = floor(uv * 5.0);
+ vec2 f = fract(uv * 5.0);
+ float md = 8.0;
+ vec2 id;
+ for (int j = -1; j <= 1; j++)
+ for (int i = -1; i <= 1; i++) {
+ vec2 g = vec2(float(i), float(j));
+ vec2 o = hash2(n + g);
+ o = 0.5 + 0.45 * sin(iTime * 0.5 + 6.283 * o);
+ vec2 r = g + o - f;
+ float d = dot(r, r);
+ if (d < md) { md = d; id = n + g; }
+ }
+ float h = fract(sin(dot(id, vec2(127.1, 311.7))) * 43758.5453);
+ vec3 col = 0.5 + 0.5 * cos(h * 6.283 + vec3(0, 2, 4));
+ col *= 0.7 + 0.3 * smoothstep(0.0, 0.05, md);
+ float edge = smoothstep(0.01, 0.02, md);
+ col *= 0.2 + 0.8 * edge;
+ fragColor = vec4(col, 1.0);
+}""", ["stained-glass", "voronoi", "colorful", "mosaic", "church", "art"]),
+ ("Zen Sand Garden", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float a = atan(uv.y, uv.x);
+ float rake = sin(r * 40.0 + a * 0.5 - iTime * 0.3);
+ float stone = smoothstep(0.15, 0.14, length(uv - vec2(0.3, 0.1)));
+ stone += smoothstep(0.08, 0.07, length(uv + vec2(0.2, 0.15)));
+ rake *= (1.0 - stone);
+ vec3 sand = vec3(0.85, 0.78, 0.65);
+ vec3 col = sand + vec3(0.05) * rake;
+ col = mix(col, vec3(0.3, 0.3, 0.28), stone);
+ fragColor = vec4(col, 1.0);
+}""", ["zen", "sand", "garden", "minimal", "japanese", "calm", "meditation"]),
+ ("Wave Interference 2D", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float v = 0.0;
+ vec2 sources[4];
+ sources[0] = vec2(sin(iTime), cos(iTime)) * 0.4;
+ sources[1] = vec2(-sin(iTime * 0.7), sin(iTime * 0.5)) * 0.5;
+ sources[2] = vec2(cos(iTime * 0.3), -sin(iTime * 0.8)) * 0.3;
+ sources[3] = vec2(-cos(iTime * 0.6), cos(iTime * 0.4)) * 0.35;
+ for (int i = 0; i < 4; i++) {
+ float d = length(uv - sources[i]);
+ v += sin(d * 30.0 - iTime * 5.0) / (1.0 + d * 5.0);
+ }
+ vec3 col = 0.5 + 0.5 * cos(v * 3.0 + vec3(0, 2, 4));
+ fragColor = vec4(col, 1.0);
+}""", ["wave", "interference", "physics", "ripple", "colorful", "multi-source"]),
+ ("DNA Helix", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ uv.y = uv.y * 2.0 - 1.0;
+ float t = iTime * 2.0;
+ float x = uv.x * 10.0 + t;
+ float strand1 = sin(x) * 0.3;
+ float strand2 = -sin(x) * 0.3;
+ float d1 = abs(uv.y - strand1);
+ float d2 = abs(uv.y - strand2);
+ float line1 = 0.005 / (d1 + 0.005);
+ float line2 = 0.005 / (d2 + 0.005);
+ float rung = 0.0;
+ float rungX = fract(x / 3.14159 * 0.5);
+ if (rungX < 0.05 || rungX > 0.95) {
+ float dy = uv.y;
+ if (dy > min(strand1, strand2) && dy < max(strand1, strand2))
+ rung = 0.5;
+ }
+ vec3 col = vec3(0.02, 0.0, 0.05);
+ col += vec3(0.0, 0.6, 1.0) * line1;
+ col += vec3(1.0, 0.3, 0.5) * line2;
+ col += vec3(0.3, 0.8, 0.4) * rung;
+ fragColor = vec4(col, 1.0);
+}""", ["dna", "helix", "biology", "science", "double-helix", "blue-pink"]),
+ ("Pendulum Wave", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec3 col = vec3(0.02, 0.01, 0.04);
+ for (int i = 0; i < 20; i++) {
+ float fi = float(i);
+ float freq = 10.0 + fi * 0.5;
+ float phase = sin(iTime * freq * 0.1) * 0.3;
+ float x = (fi + 0.5) / 20.0;
+ float y = 0.5 + phase;
+ float d = length(vec2(uv.x - x, uv.y - y) * vec2(1.0, 2.0));
+ col += vec3(0.5 + 0.5 * cos(fi * 0.3 + vec3(0, 2, 4))) * 0.005 / (d + 0.005);
+ float rod = smoothstep(0.003, 0.0, abs(uv.x - x)) * step(y, uv.y) * step(uv.y, 0.95);
+ col += vec3(0.15) * rod;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["pendulum", "wave", "physics", "harmonic", "colorful", "simulation"]),
+ ("ASCII Art Rain", """
+float char(int n, vec2 p) {
+ p = floor(p * vec2(4.0, -4.0) + 2.5);
+ if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) {
+ int bit = int(p.x) + int(p.y) * 5;
+ if (((n >> bit) & 1) == 1) return 1.0;
+ }
+ return 0.0;
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec2 grid = floor(uv * vec2(40.0, 25.0));
+ float t = floor(iTime * 10.0);
+ float drop = fract(sin(dot(grid.x + t * 0.01, 78.233)) * 43758.5) * 25.0;
+ float fade = smoothstep(drop, drop - 8.0, grid.y);
+ float bright = step(abs(grid.y - drop), 0.5);
+ vec3 col = vec3(0.0, 0.4, 0.15) * fade + vec3(0.0, 0.9, 0.4) * bright;
+ fragColor = vec4(col, 1.0);
+}""", ["ascii", "rain", "text", "matrix", "digital", "green", "code"]),
+ ("Smoke Wisps", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.3;
+ vec2 p = uv * 3.0;
+ p.y += t;
+ float n = noise(p) * 0.5 + noise(p * 2.0 + t) * 0.25 + noise(p * 4.0 - t * 0.5) * 0.125;
+ float wisp = pow(n, 3.0) * smoothstep(0.0, 0.5, uv.y) * smoothstep(1.0, 0.5, uv.y);
+ wisp *= smoothstep(0.0, 0.3, uv.x) * smoothstep(1.0, 0.7, uv.x);
+ vec3 col = vec3(wisp * 0.6);
+ col += vec3(0.02, 0.01, 0.03);
+ fragColor = vec4(col, 1.0);
+}""", ["smoke", "wisps", "atmospheric", "grey", "subtle", "elegant", "minimal"]),
+]
+
+for name, code, extra_tags in patterns:
+ s(name, code,
+ ["pattern", "2d"] + extra_tags,
+ "2d", f"{name} — procedural pattern",
+ {"chaos_level": 0.3, "color_temperature": "neutral", "motion_type": "animated"})
+
+# --- Family: Domain warping (12 shaders) ---
+warp_names = [
+ ("Acid Trip", 0.9, "vec3(1.0,0.2,0.8)", "vec3(0.1,0.9,0.3)", ["acid", "psychedelic", "trippy", "wild"]),
+ ("Deep Dream", 0.7, "vec3(0.3,0.1,0.6)", "vec3(0.8,0.6,0.2)", ["dream", "deep", "surreal", "abstract"]),
+ ("Oil Slick", 0.4, "vec3(0.2,0.5,0.8)", "vec3(0.8,0.3,0.5)", ["oil", "iridescent", "rainbow", "surface"]),
+ ("Lava Rift", 0.8, "vec3(1.0,0.3,0.0)", "vec3(0.1,0.0,0.0)", ["lava", "rift", "crack", "hot"]),
+ ("Ghost Veil", 0.3, "vec3(0.7,0.7,0.8)", "vec3(0.1,0.1,0.15)", ["ghost", "veil", "ethereal", "pale"]),
+ ("Coral Growth", 0.5, "vec3(0.9,0.4,0.5)", "vec3(0.1,0.3,0.4)", ["coral", "growth", "organic", "living"]),
+ ("Storm Eye", 0.85, "vec3(0.3,0.4,0.6)", "vec3(0.05,0.05,0.1)", ["storm", "eye", "cyclone", "dramatic"]),
+ ("Crystal Melt", 0.6, "vec3(0.6,0.8,1.0)", "vec3(0.2,0.1,0.3)", ["crystal", "melt", "dissolve", "transformation"]),
+ ("Plasma Web", 0.75, "vec3(0.0,1.0,0.8)", "vec3(0.3,0.0,0.5)", ["plasma", "web", "interconnected", "energy"]),
+ ("Sand Ripple", 0.2, "vec3(0.9,0.8,0.6)", "vec3(0.6,0.5,0.3)", ["sand", "ripple", "desert", "zen"]),
+ ("Cosmic Dust", 0.65, "vec3(0.5,0.4,0.8)", "vec3(0.05,0.02,0.1)", ["cosmic", "dust", "space", "purple"]),
+ ("Magma Pool", 0.9, "vec3(1.0,0.5,0.0)", "vec3(0.2,0.0,0.0)", ["magma", "pool", "volcanic", "orange"]),
+]
+
+for i, (name, chaos, c1, c2, extra_tags) in enumerate(warp_names):
+ warp_freq = 2.0 + (i % 5)
+ warp_amp = 0.5 + chaos * 0.5
+ code = f"""float hash(vec2 p) {{ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }}
+float noise(vec2 p) {{
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i + vec2(1, 0)), f.x),
+ mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), f.x), f.y);
+}}
+float fbm(vec2 p) {{
+ float v = 0.0, a = 0.5;
+ for (int i = 0; i < 6; i++) {{ v += a * noise(p); p *= 2.0; a *= 0.5; }}
+ return v;
+}}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.2;
+ vec2 p = uv * {warp_freq:.1f};
+ float warp1 = fbm(p + t);
+ float warp2 = fbm(p + warp1 * {warp_amp:.1f} + t * 0.7);
+ float warp3 = fbm(p + warp2 * {warp_amp:.1f} - t * 0.3);
+ vec3 col = mix({c2}, {c1}, warp3);
+ col = pow(col, vec3(0.9));
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["domain-warp", "fbm", "fluid", "animated"] + extra_tags,
+ "2d", f"Triple-layer domain warping: {name.lower()}",
+ {"chaos_level": chaos, "color_temperature": ["warm", "cool", "neutral"][i % 3], "motion_type": "fluid"})
+
+# --- Family: Raymarched scenes (10 shaders) ---
+scenes_3d = [
+ ("Floating Islands", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i+vec2(1,0)), f.x), mix(hash(i+vec2(0,1)), hash(i+vec2(1,1)), f.x), f.y);
+}
+float map(vec3 p) {
+ float ground = p.y + 1.0;
+ float island1 = length(p - vec3(0, sin(iTime)*0.3, 0)) - 1.0;
+ float island2 = length(p - vec3(2.5, cos(iTime*0.7)*0.4+0.5, 1.0)) - 0.6;
+ float island3 = length(p - vec3(-2.0, sin(iTime*0.5)*0.2+0.3, -0.5)) - 0.8;
+ return min(min(island1, island2), island3);
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy),map(p+e.yxy)-map(p-e.yxy),map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, -5);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ vec3 col = mix(vec3(0.3, 0.5, 0.9), vec3(0.7, 0.8, 1.0), uv.y + 0.5);
+ for (int i = 0; i < 80; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ float sun = max(dot(n, normalize(vec3(1, 0.8, -0.5))), 0.0);
+ col = vec3(0.3, 0.6, 0.2) * (0.3 + 0.7 * sun);
+ col = mix(col, vec3(0.5, 0.7, 1.0), 1.0 - exp(-t * 0.05));
+ break;
+ }
+ t += d;
+ if (t > 30.0) break;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["floating", "islands", "3d", "fantasy", "sky", "peaceful", "raymarching"]),
+ ("Endless Staircase", """
+float map(vec3 p) {
+ p.y += iTime * 0.5;
+ vec3 q = mod(p + 1.0, 2.0) - 1.0;
+ float box = max(max(abs(q.x) - 0.8, abs(q.y) - 0.1), abs(q.z) - 0.4);
+ float step1 = max(max(abs(q.x) - 0.3, abs(q.y + 0.3) - 0.1), abs(q.z - 0.2) - 0.4);
+ return min(box, step1);
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy),map(p+e.yxy)-map(p-e.yxy),map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, -4);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float a = iTime * 0.2;
+ rd.xz = mat2(cos(a), sin(a), -sin(a), cos(a)) * rd.xz;
+ float t = 0.0;
+ vec3 col = vec3(0.05);
+ for (int i = 0; i < 80; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ float light = max(dot(n, normalize(vec3(1, 1, -1))), 0.0);
+ col = vec3(0.7, 0.7, 0.75) * (0.2 + 0.8 * light);
+ col = mix(col, vec3(0.05), 1.0 - exp(-t * 0.08));
+ break;
+ }
+ t += d;
+ if (t > 20.0) break;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["staircase", "escher", "infinite", "3d", "architectural", "surreal", "impossible"]),
+ ("Crystal Cave", """
+float map(vec3 p) {
+ float cave = -(length(p.xz) - 3.0);
+ float crystal = 1e10;
+ for (int i = 0; i < 6; i++) {
+ float fi = float(i);
+ vec3 cp = vec3(sin(fi*1.0)*2.0, fi*0.5-1.0, cos(fi*1.0)*2.0);
+ float h = max(abs(p.x-cp.x)+abs(p.z-cp.z)-0.15, abs(p.y-cp.y)-0.4-fi*0.1);
+ crystal = min(crystal, h);
+ }
+ return max(cave, -crystal);
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy),map(p+e.yxy)-map(p-e.yxy),map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float a = iTime * 0.3;
+ vec3 ro = vec3(sin(a)*2.0, 0.0, cos(a)*2.0);
+ vec3 ta = vec3(0.0, 0.0, 0.0);
+ vec3 fwd = normalize(ta - ro);
+ vec3 right = normalize(cross(vec3(0,1,0), fwd));
+ vec3 up = cross(fwd, right);
+ vec3 rd = normalize(uv.x * right + uv.y * up + 1.5 * fwd);
+ float t = 0.0;
+ vec3 col = vec3(0.01, 0.0, 0.02);
+ for (int i = 0; i < 80; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ float light = max(dot(n, normalize(vec3(0, 1, 0))), 0.0);
+ float fresnel = pow(1.0 - abs(dot(n, rd)), 3.0);
+ col = vec3(0.2, 0.5, 0.8) * light + vec3(0.5, 0.3, 0.9) * fresnel;
+ break;
+ }
+ t += d;
+ if (t > 20.0) break;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["crystal", "cave", "3d", "underground", "fantasy", "blue", "raymarching"]),
+]
+
+for name, code, extra_tags in scenes_3d:
+ s(name, code, extra_tags, "3d", f"Raymarched 3D scene: {name}",
+ {"chaos_level": 0.4, "color_temperature": "cool", "motion_type": "orbiting"})
+
+# --- Family: Glitch/distortion effects (10 shaders) ---
+glitch_variants = [
+ ("VHS Tracking", 0.01, 8.0, ["vhs", "tracking", "analog", "retro", "distortion"]),
+ ("CRT Phosphor", 0.005, 3.0, ["crt", "phosphor", "monitor", "vintage", "scanline"]),
+ ("Databend", 0.02, 15.0, ["databend", "corruption", "digital-art", "experimental"]),
+ ("Signal Loss", 0.015, 6.0, ["signal", "loss", "static", "broadcast", "error"]),
+ ("Pixel Sort", 0.008, 10.0, ["pixel-sort", "glitch-art", "sorting", "avant-garde"]),
+ ("Bitcrush", 0.025, 4.0, ["bitcrush", "low-res", "lo-fi", "8bit", "quantize"]),
+ ("Chroma Aberration", 0.012, 5.0, ["chroma", "aberration", "lens", "rgb", "optics"]),
+ ("Digital Decay", 0.018, 12.0, ["digital-decay", "entropy", "corruption", "time"]),
+ ("Broadcast Storm", 0.03, 7.0, ["broadcast", "storm", "interference", "signal"]),
+ ("Codec Artifact", 0.02, 9.0, ["codec", "artifact", "compression", "jpeg", "macro-block"]),
+]
+
+for i, (name, intensity, freq, extra_tags) in enumerate(glitch_variants):
+ color_shift = ["vec3(0.0, 0.8, 0.3)", "vec3(0.8, 0.2, 0.5)", "vec3(0.2, 0.5, 1.0)",
+ "vec3(1.0, 0.6, 0.0)", "vec3(0.6, 0.0, 0.9)"][i % 5]
+ code = f"""float hash(float n) {{ return fract(sin(n) * 43758.5453); }}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = floor(iTime * {freq:.1f});
+ float glitch = step(0.8, hash(t + uv.y * 10.0));
+ float blockY = floor(uv.y * 15.0);
+ float shift = hash(blockY + t) * {intensity * 20:.2f} * glitch;
+ uv.x = fract(uv.x + shift);
+ float scanline = sin(uv.y * 400.0 + iTime * 30.0) * 0.03;
+ float noise = hash(uv.y * 100.0 + t) * glitch * 0.2;
+ vec3 col;
+ col.r = 0.5 + 0.5 * sin(uv.x * 6.283 * 3.0 + iTime + {float(i):.1f});
+ col.g = 0.5 + 0.5 * sin(uv.x * 6.283 * 3.0 + iTime * 1.1 + 2.0 + {float(i):.1f});
+ col.b = 0.5 + 0.5 * sin(uv.x * 6.283 * 3.0 + iTime * 0.9 + 4.0 + {float(i):.1f});
+ col += scanline + noise;
+ col *= 0.7 + 0.3 * (1.0 - glitch);
+ col += {color_shift} * glitch * 0.3;
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["glitch", "distortion", "digital", "animated"] + extra_tags,
+ "2d", f"Glitch effect: {name.lower()}",
+ {"chaos_level": 0.7 + (i % 3) * 0.1, "color_temperature": "neutral", "motion_type": "chaotic"})
+
+# --- Family: Minimalist geometric (10 shaders) ---
+minimals = [
+ ("Single Circle", "smoothstep(0.302, 0.3, length(uv))", "vec3(0.95)", "vec3(0.05)", ["circle", "minimal", "clean"]),
+ ("Double Ring", "smoothstep(0.01,0.0,abs(length(uv)-0.3))+smoothstep(0.01,0.0,abs(length(uv)-0.5))", "vec3(0.9)", "vec3(0.05)", ["rings", "double", "minimal"]),
+ ("Moving Dot", "smoothstep(0.06,0.05,length(uv-vec2(sin(iTime)*0.3,cos(iTime*0.7)*0.3)))", "vec3(1.0,0.3,0.2)", "vec3(0.02)", ["dot", "motion", "minimal"]),
+ ("Cross Hair", "smoothstep(0.005,0.0,min(abs(uv.x),abs(uv.y)))*smoothstep(0.5,0.0,length(uv))", "vec3(0.9,0.1,0.1)", "vec3(0.02)", ["crosshair", "target", "minimal"]),
+ ("Breathing Square", "step(max(abs(uv.x),abs(uv.y)),0.2+sin(iTime)*0.1)", "vec3(0.9)", "vec3(0.05)", ["square", "breathing", "minimal"]),
+ ("Line Scan", "smoothstep(0.005,0.0,abs(uv.x-sin(iTime)*0.5))", "vec3(0.0,0.8,1.0)", "vec3(0.02)", ["line", "scan", "minimal"]),
+ ("Dot Grid", "smoothstep(0.05,0.04,length(fract(uv*5.0+0.5)-0.5))", "vec3(0.3,0.3,0.35)", "vec3(0.05)", ["grid", "dots", "minimal"]),
+ ("Gradient Only", "uv.y*0.5+0.5", "vec3(0.1,0.1,0.3)", "vec3(0.0)", ["gradient", "simple", "minimal"]),
+ ("Orbit", "smoothstep(0.04,0.03,length(uv-0.3*vec2(cos(iTime),sin(iTime))))", "vec3(1.0,0.8,0.2)", "vec3(0.03,0.01,0.05)", ["orbit", "planet", "minimal"]),
+ ("Pulse Ring", "smoothstep(0.01,0.0,abs(length(uv)-(mod(iTime,2.0)*0.5)))*exp(-mod(iTime,2.0)*2.0)", "vec3(0.0,0.9,0.5)", "vec3(0.02)", ["pulse", "ring", "expanding", "minimal"]),
+]
+
+for i, (name, expr, fg, bg, extra_tags) in enumerate(minimals):
+ code = f"""void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float v = {expr};
+ vec3 col = mix({bg}, {fg}, v);
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["minimal", "geometric", "clean", "simple"] + extra_tags,
+ "2d", f"Minimalist: {name.lower()}",
+ {"chaos_level": 0.05 + (i % 3) * 0.05, "color_temperature": ["monochrome", "cool", "warm"][i % 3], "motion_type": "static" if i % 3 == 0 else "animated"})
+
+# --- Family: More 3D environments (8 shaders) ---
+more_3d = [
+ ("Infinite Columns", """
+float map(vec3 p) {
+ vec2 q = mod(p.xz + 1.0, 2.0) - 1.0;
+ float col = length(q) - 0.2;
+ float floor_d = p.y + 1.5;
+ float ceil_d = -(p.y - 3.0);
+ return min(min(col, floor_d), ceil_d);
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy),map(p+e.yxy)-map(p-e.yxy),map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(iTime * 0.5, 0.0, iTime * 0.3);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ vec3 col = vec3(0.02);
+ for (int i = 0; i < 80; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ float light = max(dot(n, normalize(vec3(1, 1, -1))), 0.0);
+ col = vec3(0.8, 0.75, 0.7) * (0.15 + 0.85 * light);
+ col = mix(col, vec3(0.02), 1.0 - exp(-t * 0.06));
+ break;
+ }
+ t += d;
+ if (t > 30.0) break;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["columns", "infinite", "3d", "architectural", "ancient", "temple"]),
+ ("Wormhole", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float a = atan(uv.y, uv.x);
+ float tunnel = 1.0 / (r + 0.1);
+ float twist = a + tunnel * 0.5 + iTime;
+ float tex = sin(twist * 4.0) * sin(tunnel * 3.0 - iTime * 4.0);
+ float glow = exp(-r * 2.0);
+ vec3 col = vec3(0.02, 0.0, 0.05);
+ col += vec3(0.2, 0.5, 1.0) * tex * glow;
+ col += vec3(0.8, 0.4, 1.0) * glow * 0.3;
+ col += vec3(1.0, 0.9, 0.7) * exp(-r * 8.0) * 0.5;
+ fragColor = vec4(max(col, 0.0), 1.0);
+}""", ["wormhole", "tunnel", "space", "3d-illusion", "portal", "sci-fi", "blue"]),
+ ("Cubic Lattice", """
+float map(vec3 p) {
+ vec3 q = mod(p + 0.5, 1.0) - 0.5;
+ float bars = min(min(
+ max(abs(q.x), abs(q.y)) - 0.08,
+ max(abs(q.y), abs(q.z)) - 0.08),
+ max(abs(q.x), abs(q.z)) - 0.08);
+ return bars;
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy),map(p+e.yxy)-map(p-e.yxy),map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime * 0.3;
+ vec3 ro = vec3(sin(t)*3.0, 1.0+sin(t*0.7), cos(t)*3.0);
+ vec3 ta = vec3(0.0);
+ vec3 fwd = normalize(ta - ro);
+ vec3 right = normalize(cross(vec3(0,1,0), fwd));
+ vec3 up = cross(fwd, right);
+ vec3 rd = normalize(uv.x*right + uv.y*up + 1.5*fwd);
+ float d_t = 0.0;
+ vec3 col = vec3(0.02);
+ for (int i = 0; i < 80; i++) {
+ float d = map(ro + rd * d_t);
+ if (d < 0.001) {
+ vec3 p = ro + rd * d_t;
+ vec3 n = getNormal(p);
+ float light = max(dot(n, normalize(vec3(1,1,-1))), 0.0);
+ col = vec3(0.9, 0.5, 0.2) * (0.2 + 0.8 * light);
+ col = mix(col, vec3(0.02), 1.0 - exp(-d_t * 0.1));
+ break;
+ }
+ d_t += d;
+ if (d_t > 20.0) break;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["lattice", "cubic", "3d", "wireframe", "structural", "orange", "geometric"]),
+]
+
+for name, code, extra_tags in more_3d:
+ s(name, code, extra_tags, "3d", f"3D scene: {name}",
+ {"chaos_level": 0.4, "color_temperature": "cool", "motion_type": "forward"})
+
+# --- Family: Color theory experiments (10 shaders) ---
+color_exps = [
+ ("RGB Separation", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime;
+ float r = length(uv - vec2(sin(t)*0.2, cos(t)*0.15)) - 0.3;
+ float g = length(uv - vec2(sin(t*1.1+2.0)*0.2, cos(t*0.9+1.0)*0.15)) - 0.3;
+ float b = length(uv - vec2(sin(t*0.8+4.0)*0.2, cos(t*1.2+3.0)*0.15)) - 0.3;
+ vec3 col;
+ col.r = smoothstep(0.01, 0.0, r);
+ col.g = smoothstep(0.01, 0.0, g);
+ col.b = smoothstep(0.01, 0.0, b);
+ fragColor = vec4(col, 1.0);
+}""", ["rgb", "separation", "additive", "color-theory", "circles", "primary-colors"]),
+ ("CMY Overlap", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float t = iTime * 0.5;
+ float c = smoothstep(0.01, 0.0, length(uv - 0.15*vec2(sin(t), cos(t))) - 0.25);
+ float m = smoothstep(0.01, 0.0, length(uv - 0.15*vec2(sin(t+2.1), cos(t+2.1))) - 0.25);
+ float y = smoothstep(0.01, 0.0, length(uv - 0.15*vec2(sin(t+4.2), cos(t+4.2))) - 0.25);
+ vec3 col = vec3(1.0) - vec3(c, 0.0, 0.0) - vec3(0.0, m, 0.0) - vec3(0.0, 0.0, y);
+ col = max(col, 0.0);
+ col = mix(vec3(0.95), col, max(max(c, m), y));
+ fragColor = vec4(col, 1.0);
+}""", ["cmy", "subtractive", "color-theory", "overlap", "pastel", "print"]),
+ ("Hue Wheel", """
+vec3 hsv(float h, float s, float v) {
+ vec3 c = clamp(abs(mod(h*6.0+vec3(0,4,2),6.0)-3.0)-1.0, 0.0, 1.0);
+ return v * mix(vec3(1.0), c, s);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float a = atan(uv.y, uv.x) / 6.2832 + 0.5;
+ float r = length(uv);
+ vec3 col = hsv(a + iTime * 0.1, smoothstep(0.1, 0.4, r), smoothstep(0.5, 0.2, r));
+ float ring = smoothstep(0.01, 0.0, abs(r - 0.35));
+ col += ring * 0.3;
+ fragColor = vec4(col, 1.0);
+}""", ["hue", "wheel", "color-theory", "hsv", "rainbow", "spectrum", "educational"]),
+]
+
+for name, code, extra_tags in color_exps:
+ s(name, code, extra_tags, "2d", f"Color theory: {name}",
+ {"chaos_level": 0.2, "color_temperature": "neutral", "motion_type": "animated"})
+
+# --- Family: Particle systems (7 shaders) ---
+particle_types = [
+ ("Fireflies", "vec3(1.0,0.9,0.3)", 0.003, 30, ["fireflies", "night", "gentle", "nature"]),
+ ("Snow Fall", "vec3(0.9,0.95,1.0)", 0.004, 50, ["snow", "winter", "cold", "peaceful"]),
+ ("Ember Rise", "vec3(1.0,0.4,0.0)", 0.003, 40, ["ember", "fire", "rising", "warm"]),
+ ("Dust Motes", "vec3(0.8,0.7,0.5)", 0.005, 25, ["dust", "motes", "sunbeam", "atmospheric"]),
+ ("Pollen Drift", "vec3(0.9,0.8,0.2)", 0.006, 20, ["pollen", "spring", "floating", "organic"]),
+ ("Star Rain", "vec3(0.8,0.9,1.0)", 0.002, 60, ["stars", "rain", "cosmic", "shower"]),
+ ("Bubble Float", "vec3(0.5,0.8,1.0)", 0.008, 15, ["bubbles", "float", "underwater", "playful"]),
+]
+
+for i, (name, color, size, count, extra_tags) in enumerate(particle_types):
+ code = f"""float hash(float n) {{ return fract(sin(n) * 43758.5453); }}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {{
+ vec2 uv = fragCoord / iResolution.xy;
+ vec3 col = vec3(0.02, 0.01, 0.04);
+ for (int i = 0; i < {count}; i++) {{
+ float fi = float(i);
+ float seed = fi * 137.0 + {float(i * 17):.1f};
+ vec2 pos;
+ pos.x = hash(seed);
+ pos.y = fract(hash(seed + 1.0) + iTime * (0.02 + hash(seed + 2.0) * 0.04) * {1.0 if i % 2 == 0 else -1.0:.1f});
+ pos.x += sin(iTime * hash(seed + 3.0) + fi) * 0.02;
+ float d = length(uv - pos);
+ float brightness = hash(seed + 4.0) * 0.5 + 0.5;
+ float twinkle = sin(iTime * (2.0 + hash(seed + 5.0) * 3.0) + fi) * 0.3 + 0.7;
+ col += {color} * ({size} / (d + {size})) * brightness * twinkle;
+ }}
+ fragColor = vec4(col, 1.0);
+}}"""
+ s(name, code,
+ ["particles", "floating", "ambient", "animated"] + extra_tags,
+ "2d", f"Ambient particle system: {name.lower()}",
+ {"chaos_level": 0.2 + (i % 3) * 0.1, "color_temperature": ["warm", "cool", "neutral"][i % 3], "motion_type": "floating"})
+
+# --- Family: More distinct 3D (10 shaders) ---
+distinct_3d = [
+ ("Infinite Corridor", """
+float map(vec3 p) {
+ vec2 q = abs(mod(p.xz, 4.0) - 2.0);
+ float walls = min(q.x, q.y) - 0.1;
+ float ceiling = abs(p.y) - 2.0;
+ return max(-walls, ceiling);
+}
+vec3 getNormal(vec3 p) {
+ vec2 e = vec2(0.001, 0);
+ return normalize(vec3(map(p+e.xyy)-map(p-e.xyy),map(p+e.yxy)-map(p-e.yxy),map(p+e.yyx)-map(p-e.yyx)));
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, iTime);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ vec3 col = vec3(0.02);
+ for (int i = 0; i < 80; i++) {
+ float d = map(ro + rd * t);
+ if (abs(d) < 0.001) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ col = vec3(0.6, 0.7, 0.8) * (0.2 + 0.8 * max(dot(n, vec3(0, 1, 0)), 0.0));
+ col *= 1.0 + 0.1 * sin(p.z);
+ col = mix(col, vec3(0.02), 1.0 - exp(-t * 0.04));
+ break;
+ }
+ t += abs(d);
+ if (t > 30.0) break;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["corridor", "hallway", "infinite", "3d", "architectural", "perspective"]),
+ ("Planet Surface", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i+vec2(1,0)), f.x), mix(hash(i+vec2(0,1)), hash(i+vec2(1,1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float planet_r = 0.4;
+ vec3 col = vec3(0.0, 0.0, 0.02);
+ if (r < planet_r) {
+ float z = sqrt(planet_r * planet_r - r * r);
+ vec3 n = normalize(vec3(uv, z));
+ vec2 texUV = vec2(atan(n.x, n.z) + iTime * 0.2, asin(n.y));
+ float land = noise(texUV * 5.0) * 0.5 + noise(texUV * 10.0) * 0.25;
+ vec3 surface = mix(vec3(0.1, 0.3, 0.8), vec3(0.2, 0.6, 0.15), step(0.45, land));
+ float sun = max(dot(n, normalize(vec3(1, 0.5, 0.5))), 0.0);
+ col = surface * (0.1 + 0.9 * sun);
+ }
+ float atmo = smoothstep(planet_r + 0.02, planet_r - 0.02, r) * (1.0 - smoothstep(planet_r - 0.02, planet_r + 0.08, r));
+ col += vec3(0.3, 0.5, 1.0) * atmo * 0.3;
+ col += vec3(0.8, 0.9, 1.0) * 0.001 / (abs(r - planet_r) + 0.005);
+ fragColor = vec4(col, 1.0);
+}""", ["planet", "earth", "space", "3d", "sphere", "globe", "rotating"]),
+ ("Disco Ball", """
+float map(vec3 p) { return length(p) - 1.0; }
+vec3 getNormal(vec3 p) { return normalize(p); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ vec3 ro = vec3(0, 0, -3);
+ vec3 rd = normalize(vec3(uv, 1.0));
+ float t = 0.0;
+ vec3 col = vec3(0.02);
+ for (int i = 0; i < 40; i++) {
+ float d = map(ro + rd * t);
+ if (d < 0.001) {
+ vec3 p = ro + rd * t;
+ vec3 n = getNormal(p);
+ float a1 = atan(n.z, n.x) + iTime * 0.5;
+ float a2 = acos(n.y);
+ float grid = step(0.9, fract(a1 * 5.0)) + step(0.9, fract(a2 * 5.0));
+ vec3 refl = reflect(rd, n);
+ float spec = pow(max(dot(refl, normalize(vec3(sin(iTime), 1, cos(iTime)))), 0.0), 32.0);
+ float mirror = 0.5 + 0.5 * fract(sin(dot(floor(vec2(a1, a2) * 5.0), vec2(127.1, 311.7))) * 43758.5);
+ col = vec3(0.3) * (1.0 - grid * 0.5) + vec3(1.0) * spec * mirror;
+ col += vec3(0.8, 0.3, 0.9) * spec * 0.3;
+ break;
+ }
+ t += d;
+ if (t > 10.0) break;
+ }
+ fragColor = vec4(col, 1.0);
+}""", ["disco", "ball", "mirror", "3d", "party", "retro", "reflective", "fun"]),
+]
+
+for name, code, extra_tags in distinct_3d:
+ s(name, code, extra_tags, "3d", f"3D scene: {name}",
+ {"chaos_level": 0.4, "color_temperature": "cool", "motion_type": "rotating"})
+
+# --- Standalone standouts (7 more to break 200) ---
+
+s("Supernova", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float a = atan(uv.y, uv.x);
+ float t = iTime;
+ float shockwave = smoothstep(0.02, 0.0, abs(r - mod(t * 0.5, 2.0)));
+ float core = exp(-r * 8.0);
+ float rays = pow(0.5 + 0.5 * sin(a * 12.0 + t * 3.0), 8.0) * exp(-r * 3.0);
+ vec3 col = vec3(1.0, 0.8, 0.3) * core;
+ col += vec3(1.0, 0.3, 0.1) * rays;
+ col += vec3(0.3, 0.5, 1.0) * shockwave;
+ fragColor = vec4(col, 1.0);
+}""", ["supernova", "explosion", "star", "cosmic", "energy", "bright"], "2d",
+"Supernova with expanding shockwave and ray corona",
+{"chaos_level": 0.8, "color_temperature": "warm", "motion_type": "explosive"})
+
+s("Fabric Fold", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float fold = sin(uv.x * 12.0 + iTime * 0.3) * sin(uv.y * 8.0 - iTime * 0.21) * 0.5;
+ float shadow = fold * 0.5 + 0.5;
+ vec3 col = vec3(0.6, 0.15, 0.2) * (0.4 + 0.6 * shadow);
+ col += vec3(0.3, 0.1, 0.1) * pow(max(shadow, 0.0), 4.0);
+ fragColor = vec4(col, 1.0);
+}""", ["fabric", "fold", "cloth", "textile", "red", "silk", "elegant"], "2d",
+"Folded silk fabric with dynamic shadow",
+{"chaos_level": 0.2, "color_temperature": "warm", "motion_type": "fluid"})
+
+s("EKG Heartbeat", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float x = fract(uv.x * 2.0 - iTime * 0.5);
+ float y = uv.y * 2.0 - 1.0;
+ float ekg = 0.0;
+ if (x < 0.15) ekg = (x / 0.15) * 0.3;
+ else if (x < 0.2) ekg = (0.2 - x) / 0.05 * 0.3;
+ else if (x < 0.3) ekg = (x - 0.25) * 16.0;
+ else if (x < 0.35) ekg = (0.35 - x) * 16.0 - 0.2;
+ float d = abs(y - ekg);
+ vec3 col = vec3(0.0, 1.0, 0.3) * 0.004 / (d + 0.004);
+ col += vec3(0.0, 0.3, 0.1) * 0.015 / (d + 0.015);
+ fragColor = vec4(col, 1.0);
+}""", ["ekg", "heartbeat", "medical", "pulse", "green", "monitor", "health"], "2d",
+"EKG heartbeat monitor trace",
+{"chaos_level": 0.1, "color_temperature": "cool", "motion_type": "scrolling"})
+
+s("Rotating Galaxy Arm", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float a = atan(uv.y, uv.x) + iTime * 0.2;
+ float arm = max(sin(a * 2.0 - r * 8.0) * exp(-r * 1.5), 0.0);
+ float stars = step(0.997, hash(floor(fragCoord)));
+ vec3 col = vec3(0.01, 0.0, 0.02) + vec3(0.3, 0.2, 0.5) * arm + vec3(0.8, 0.6, 0.3) * arm * arm * 2.0;
+ col += vec3(0.8, 0.85, 1.0) * stars * (1.0 - r);
+ col += vec3(1.0, 0.9, 0.6) * exp(-r * 12.0) * 0.5;
+ fragColor = vec4(col, 1.0);
+}""", ["galaxy", "spiral-arm", "space", "stars", "cosmic", "astronomy"], "2d",
+"Spiral galaxy with rotating arms and star field",
+{"chaos_level": 0.4, "color_temperature": "warm", "motion_type": "rotating"})
+
+s("Underwater Caustics", """
+float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
+float noise(vec2 p) {
+ vec2 i = floor(p); vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f);
+ return mix(mix(hash(i), hash(i+vec2(1,0)), f.x), mix(hash(i+vec2(0,1)), hash(i+vec2(1,1)), f.x), f.y);
+}
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ float t = iTime * 0.5;
+ float c = 0.0;
+ for (int i = 0; i < 3; i++) {
+ float fi = float(i);
+ vec2 p = uv * (4.0 + fi * 2.0) + vec2(t * (0.3 + fi * 0.1), t * 0.2);
+ c += abs(sin(noise(p) * 6.283)) * (0.5 / (1.0 + fi));
+ }
+ vec3 col = mix(vec3(0.0, 0.15, 0.35), vec3(0.1, 0.6, 0.8), pow(c, 1.5));
+ fragColor = vec4(col, 1.0);
+}""", ["underwater", "caustics", "water", "ocean", "blue", "ripple", "serene"], "2d",
+"Underwater light caustics on the ocean floor",
+{"chaos_level": 0.3, "color_temperature": "cool", "motion_type": "fluid"})
+
+s("Geometric Rose", """
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float r = length(uv);
+ float a = atan(uv.y, uv.x);
+ float rose = cos(a * 4.0 + iTime) * 0.3 + 0.3;
+ float inner = cos(a * 7.0 - iTime * 1.5) * 0.15 + 0.15;
+ vec3 col = vec3(0.02, 0.0, 0.04);
+ col += vec3(0.9, 0.2, 0.4) * 0.003 / (abs(r - rose) + 0.003);
+ col += vec3(1.0, 0.6, 0.7) * 0.002 / (abs(r - inner) + 0.002);
+ col += vec3(0.3, 0.05, 0.1) * exp(-r * 3.0);
+ fragColor = vec4(col, 1.0);
+}""", ["rose", "polar", "mathematical", "curve", "pink", "elegant", "botanical"], "2d",
+"Polar rose curves with counter-rotating petals",
+{"chaos_level": 0.3, "color_temperature": "warm", "motion_type": "rotating"})
+
+s("Data Stream", """
+float hash(float n) { return fract(sin(n) * 43758.5453); }
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ vec3 col = vec3(0.02, 0.01, 0.03);
+ for (int i = 0; i < 30; i++) {
+ float fi = float(i);
+ float lane = (fi + 0.5) / 30.0;
+ float speed = 0.5 + hash(fi * 7.0) * 2.0;
+ float y = fract(uv.y * 3.0 + iTime * speed + hash(fi * 13.0));
+ float on = step(0.3, hash(floor(y * 20.0) + fi * 100.0 + floor(iTime * speed * 3.0)));
+ float d = abs(uv.x - lane);
+ col += (0.5 + 0.5 * cos(fi * 0.5 + vec3(0, 2, 4))) * 0.001 / (d + 0.001) * on * y * 0.1;
+ }
+ fragColor = vec4(min(col, 1.0), 1.0);
+}""", ["data", "stream", "digital", "network", "colorful", "flow", "tech", "cyber"], "2d",
+"Multi-lane data streams at different speeds",
+{"chaos_level": 0.5, "color_temperature": "cool", "motion_type": "flowing"})
+
+
+# ═══════════════════════════════════════════════════════════
+
+print(f"Total shaders defined: {len(SHADERS)}")
+
+# --- Database insertion ---
+async def seed():
+ import asyncpg
+ import json
+ import os
+
+ db_url = os.environ.get("DATABASE_URL_SYNC", "postgresql://fracta:devpass@postgres:5432/fractafrag")
+ # Convert sync URL to asyncpg format
+ db_url = db_url.replace("postgresql://", "postgresql://").replace("+asyncpg", "")
+
+ conn = await asyncpg.connect(db_url)
+
+ # Check if already seeded
+ count = await conn.fetchval("SELECT COUNT(*) FROM shaders WHERE is_system = TRUE")
+ if count > 0:
+ print(f"Already seeded ({count} system shaders). Skipping.")
+ await conn.close()
+ return
+
+ print(f"Seeding {len(SHADERS)} shaders...")
+
+ # Distribute creation times over past 30 days for realistic feed testing
+ now = datetime.now(timezone.utc)
+ total = len(SHADERS)
+
+ for i, shader in enumerate(SHADERS):
+ age_hours = random.uniform(0, 30 * 24) # up to 30 days ago
+ created_at = now - timedelta(hours=age_hours)
+ score = random.uniform(0, 10) # random initial score for feed ranking variety
+
+ shader_id = str(uuid.uuid4())
+
+ await conn.execute("""
+ INSERT INTO shaders (id, author_id, title, description, glsl_code, status, is_public,
+ is_ai_generated, is_system, system_label, tags, shader_type,
+ style_metadata, render_status, score, current_version,
+ created_at, updated_at)
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
+ """,
+ uuid.UUID(shader_id),
+ uuid.UUID(SYSTEM_USER_ID),
+ shader["title"],
+ shader["description"],
+ shader["code"],
+ "published",
+ True,
+ False, # not AI-generated — these are hand-curated
+ True, # is_system
+ "fractafrag-curated",
+ shader["tags"],
+ shader["shader_type"],
+ json.dumps(shader["style_metadata"]),
+ "ready", # render_status — these render client-side
+ score,
+ 1,
+ created_at,
+ created_at,
+ )
+
+ # Also create version 1 for each
+ await conn.execute("""
+ INSERT INTO shader_versions (id, shader_id, version_number, glsl_code, title, description,
+ tags, style_metadata, change_note, created_at)
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
+ """,
+ uuid.uuid4(),
+ uuid.UUID(shader_id),
+ 1,
+ shader["code"],
+ shader["title"],
+ shader["description"],
+ shader["tags"],
+ json.dumps(shader["style_metadata"]),
+ "Initial version — fractafrag curated content",
+ created_at,
+ )
+
+ if (i + 1) % 25 == 0:
+ print(f" ...{i + 1}/{total}")
+
+ await conn.close()
+ print(f"Done. {total} shaders seeded as 'fractafrag-curated' system content.")
+
+if __name__ == "__main__":
+ asyncio.run(seed())
diff --git a/services/api/app/models/__init__.py b/services/api/app/models/__init__.py
index fde2343..23fbe8a 100644
--- a/services/api/app/models/__init__.py
+++ b/services/api/app/models/__init__.py
@@ -1,12 +1,14 @@
"""Models package."""
from app.models.models import (
- User, Shader, Vote, EngagementEvent, Desire, DesireCluster,
+ SYSTEM_USER_ID,
+ User, Shader, ShaderVersion, Vote, EngagementEvent, Desire, DesireCluster,
BountyTip, CreatorPayout, ApiKey, GenerationLog, Comment,
SourceUnlock, CreatorEngagementSnapshot,
)
__all__ = [
- "User", "Shader", "Vote", "EngagementEvent", "Desire", "DesireCluster",
+ "SYSTEM_USER_ID",
+ "User", "Shader", "ShaderVersion", "Vote", "EngagementEvent", "Desire", "DesireCluster",
"BountyTip", "CreatorPayout", "ApiKey", "GenerationLog", "Comment",
"SourceUnlock", "CreatorEngagementSnapshot",
]
diff --git a/services/api/app/models/models.py b/services/api/app/models/models.py
index 5f3b3f0..c0701a8 100644
--- a/services/api/app/models/models.py
+++ b/services/api/app/models/models.py
@@ -11,6 +11,9 @@ from pgvector.sqlalchemy import Vector
from sqlalchemy.orm import relationship
from app.database import Base
+# System account UUID — the "fractafrag" platform user
+SYSTEM_USER_ID = uuid.UUID("00000000-0000-0000-0000-000000000001")
+
class User(Base):
__tablename__ = "users"
@@ -21,19 +24,17 @@ class User(Base):
password_hash = Column(String, nullable=False)
role = Column(String, nullable=False, default="user")
trust_tier = Column(String, nullable=False, default="standard")
+ is_system = Column(Boolean, nullable=False, default=False)
stripe_customer_id = Column(String, nullable=True)
subscription_tier = Column(String, default="free")
ai_credits_remaining = Column(Integer, default=0)
taste_vector = Column(Vector(512), nullable=True)
- # Creator economy stubs
is_verified_creator = Column(Boolean, default=False)
verified_creator_at = Column(DateTime(timezone=True), nullable=True)
stripe_connect_account_id = Column(String, nullable=True)
- # Timestamps
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
last_active_at = Column(DateTime(timezone=True), nullable=True)
- # Relationships
shaders = relationship("Shader", back_populates="author")
votes = relationship("Vote", back_populates="user")
api_keys = relationship("ApiKey", back_populates="user")
@@ -47,9 +48,12 @@ class Shader(Base):
title = Column(String, nullable=False)
description = Column(Text, nullable=True)
glsl_code = Column(Text, nullable=False)
+ status = Column(String, nullable=False, default="published") # draft, published, archived
is_public = Column(Boolean, default=True)
is_ai_generated = Column(Boolean, default=False)
+ is_system = Column(Boolean, default=False)
ai_provider = Column(String, nullable=True)
+ system_label = Column(String, nullable=True)
thumbnail_url = Column(String, nullable=True)
preview_url = Column(String, nullable=True)
render_status = Column(String, default="pending")
@@ -58,20 +62,38 @@ class Shader(Base):
tags = Column(ARRAY(String), default=list)
shader_type = Column(String, default="2d")
forked_from = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="SET NULL"), nullable=True)
+ current_version = Column(Integer, nullable=False, default=1)
view_count = Column(Integer, default=0)
score = Column(Float, default=0.0)
- # Creator economy stubs
access_tier = Column(String, default="open")
source_unlock_price_cents = Column(Integer, nullable=True)
commercial_license_price_cents = Column(Integer, nullable=True)
verified_creator_shader = Column(Boolean, default=False)
- # Timestamps
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
- # Relationships
author = relationship("User", back_populates="shaders")
votes = relationship("Vote", back_populates="shader")
+ versions = relationship("ShaderVersion", back_populates="shader", order_by="ShaderVersion.version_number.desc()")
+
+
+class ShaderVersion(Base):
+ __tablename__ = "shader_versions"
+ __table_args__ = (UniqueConstraint("shader_id", "version_number"),)
+
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
+ shader_id = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="CASCADE"), nullable=False)
+ version_number = Column(Integer, nullable=False)
+ glsl_code = Column(Text, nullable=False)
+ title = Column(String, nullable=False)
+ description = Column(Text, nullable=True)
+ tags = Column(ARRAY(String), default=list)
+ style_metadata = Column(JSONB, nullable=True)
+ change_note = Column(Text, nullable=True)
+ thumbnail_url = Column(String, nullable=True)
+ created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
+
+ shader = relationship("Shader", back_populates="versions")
class Vote(Base):
@@ -195,7 +217,6 @@ class Comment(Base):
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
-# Creator economy stubs (dormant)
class SourceUnlock(Base):
__tablename__ = "source_unlocks"
diff --git a/services/api/app/routers/feed.py b/services/api/app/routers/feed.py
index 4f20e9e..5ad010a 100644
--- a/services/api/app/routers/feed.py
+++ b/services/api/app/routers/feed.py
@@ -11,6 +11,9 @@ from app.middleware.auth import get_optional_user, get_current_user
router = APIRouter()
+# Common filter for public, published shaders
+_FEED_FILTER = [Shader.is_public == True, Shader.status == "published"]
+
@router.get("", response_model=list[ShaderFeedItem])
async def get_feed(
@@ -19,15 +22,9 @@ async def get_feed(
db: AsyncSession = Depends(get_db),
user: User | None = Depends(get_optional_user),
):
- """
- Personalized feed for authenticated users (pgvector taste match).
- Trending/new for anonymous users.
- """
- # TODO: Implement full recommendation engine (Track F)
- # For now: return newest public shaders
query = (
select(Shader)
- .where(Shader.is_public == True, Shader.render_status == "ready")
+ .where(*_FEED_FILTER)
.order_by(Shader.created_at.desc())
.limit(limit)
)
@@ -42,7 +39,7 @@ async def get_trending(
):
query = (
select(Shader)
- .where(Shader.is_public == True, Shader.render_status == "ready")
+ .where(*_FEED_FILTER)
.order_by(Shader.score.desc())
.limit(limit)
)
@@ -57,7 +54,7 @@ async def get_new(
):
query = (
select(Shader)
- .where(Shader.is_public == True, Shader.render_status == "ready")
+ .where(*_FEED_FILTER)
.order_by(Shader.created_at.desc())
.limit(limit)
)
@@ -71,7 +68,6 @@ async def report_dwell(
db: AsyncSession = Depends(get_db),
user: User | None = Depends(get_optional_user),
):
- """Report dwell time signal for recommendation engine."""
from app.models import EngagementEvent
event = EngagementEvent(
@@ -83,4 +79,3 @@ async def report_dwell(
event_metadata={"replayed": body.replayed},
)
db.add(event)
- # TODO: Update user taste vector (Track F)
diff --git a/services/api/app/routers/shaders.py b/services/api/app/routers/shaders.py
index 75d3ea7..ba93624 100644
--- a/services/api/app/routers/shaders.py
+++ b/services/api/app/routers/shaders.py
@@ -1,4 +1,4 @@
-"""Shaders router — CRUD, submit, fork, search."""
+"""Shaders router — CRUD, versioning, drafts, fork, search."""
from uuid import UUID
from datetime import datetime, timezone
@@ -7,25 +7,31 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from app.database import get_db
-from app.models import User, Shader
-from app.schemas import ShaderCreate, ShaderUpdate, ShaderPublic
+from app.models import User, Shader, ShaderVersion
+from app.schemas import ShaderCreate, ShaderUpdate, ShaderPublic, ShaderVersionPublic
from app.middleware.auth import get_current_user, get_optional_user
from app.services.glsl_validator import validate_glsl
router = APIRouter()
+# ── Public list / search ──────────────────────────────────
+
@router.get("", response_model=list[ShaderPublic])
async def list_shaders(
q: str | None = Query(None, description="Search query"),
tags: list[str] | None = Query(None, description="Filter by tags"),
shader_type: str | None = Query(None, description="Filter by type: 2d, 3d, audio-reactive"),
sort: str = Query("trending", description="Sort: trending, new, top"),
+ is_system: bool | None = Query(None, description="Filter to system/platform shaders"),
limit: int = Query(20, ge=1, le=50),
offset: int = Query(0, ge=0),
db: AsyncSession = Depends(get_db),
):
- query = select(Shader).where(Shader.is_public == True, Shader.render_status == "ready")
+ query = select(Shader).where(
+ Shader.is_public == True,
+ Shader.status == "published",
+ )
if q:
query = query.where(Shader.title.ilike(f"%{q}%"))
@@ -33,12 +39,14 @@ async def list_shaders(
query = query.where(Shader.tags.overlap(tags))
if shader_type:
query = query.where(Shader.shader_type == shader_type)
+ if is_system is not None:
+ query = query.where(Shader.is_system == is_system)
if sort == "new":
query = query.order_by(Shader.created_at.desc())
elif sort == "top":
query = query.order_by(Shader.score.desc())
- else: # trending
+ else:
query = query.order_by(Shader.score.desc(), Shader.created_at.desc())
query = query.limit(limit).offset(offset)
@@ -46,6 +54,27 @@ async def list_shaders(
return result.scalars().all()
+# ── My shaders (workspace) ───────────────────────────────
+
+@router.get("/mine", response_model=list[ShaderPublic])
+async def my_shaders(
+ status_filter: str | None = Query(None, alias="status", description="draft, published, archived"),
+ limit: int = Query(50, ge=1, le=100),
+ offset: int = Query(0, ge=0),
+ db: AsyncSession = Depends(get_db),
+ user: User = Depends(get_current_user),
+):
+ """List the authenticated user's shaders — drafts, published, archived."""
+ query = select(Shader).where(Shader.author_id == user.id)
+ if status_filter:
+ query = query.where(Shader.status == status_filter)
+ query = query.order_by(Shader.updated_at.desc()).limit(limit).offset(offset)
+ result = await db.execute(query)
+ return result.scalars().all()
+
+
+# ── Single shader ─────────────────────────────────────────
+
@router.get("/{shader_id}", response_model=ShaderPublic)
async def get_shader(
shader_id: UUID,
@@ -57,47 +86,87 @@ async def get_shader(
if not shader:
raise HTTPException(status_code=404, detail="Shader not found")
+ # Drafts are only visible to their author
+ if shader.status == "draft" and (not user or user.id != shader.author_id):
+ raise HTTPException(status_code=404, detail="Shader not found")
if not shader.is_public and (not user or user.id != shader.author_id):
raise HTTPException(status_code=404, detail="Shader not found")
- # Increment view count
shader.view_count += 1
return shader
+# ── Version history ───────────────────────────────────────
+
+@router.get("/{shader_id}/versions", response_model=list[ShaderVersionPublic])
+async def list_versions(
+ shader_id: UUID,
+ db: AsyncSession = Depends(get_db),
+ user: User | None = Depends(get_optional_user),
+):
+ """Get the version history of a shader."""
+ shader = (await db.execute(select(Shader).where(Shader.id == shader_id))).scalar_one_or_none()
+ if not shader:
+ raise HTTPException(status_code=404, detail="Shader not found")
+ if shader.status == "draft" and (not user or user.id != shader.author_id):
+ raise HTTPException(status_code=404, detail="Shader not found")
+
+ result = await db.execute(
+ select(ShaderVersion)
+ .where(ShaderVersion.shader_id == shader_id)
+ .order_by(ShaderVersion.version_number.desc())
+ )
+ return result.scalars().all()
+
+
+@router.get("/{shader_id}/versions/{version_number}", response_model=ShaderVersionPublic)
+async def get_version(
+ shader_id: UUID,
+ version_number: int,
+ db: AsyncSession = Depends(get_db),
+):
+ result = await db.execute(
+ select(ShaderVersion).where(
+ ShaderVersion.shader_id == shader_id,
+ ShaderVersion.version_number == version_number,
+ )
+ )
+ version = result.scalar_one_or_none()
+ if not version:
+ raise HTTPException(status_code=404, detail="Version not found")
+ return version
+
+
+# ── Create shader (draft or published) ───────────────────
+
@router.post("", response_model=ShaderPublic, status_code=status.HTTP_201_CREATED)
async def create_shader(
body: ShaderCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
- # Rate limit: free tier gets 5 submissions/month
- if user.subscription_tier == "free":
+ # Rate limit published shaders for free tier (drafts are unlimited)
+ if body.status == "published" and user.subscription_tier == "free":
month_start = datetime.now(timezone.utc).replace(day=1, hour=0, minute=0, second=0, microsecond=0)
count_result = await db.execute(
select(func.count()).select_from(Shader).where(
Shader.author_id == user.id,
+ Shader.status == "published",
Shader.created_at >= month_start,
)
)
monthly_count = count_result.scalar()
if monthly_count >= 5:
- raise HTTPException(
- status_code=429,
- detail="Free tier: 5 shader submissions per month. Upgrade to Pro for unlimited."
- )
+ raise HTTPException(status_code=429, detail="Free tier: 5 published shaders/month. Upgrade to Pro for unlimited.")
# Validate GLSL
validation = validate_glsl(body.glsl_code, body.shader_type)
if not validation.valid:
- raise HTTPException(
- status_code=422,
- detail={
- "message": "GLSL validation failed",
- "errors": validation.errors,
- "warnings": validation.warnings,
- }
- )
+ raise HTTPException(status_code=422, detail={
+ "message": "GLSL validation failed",
+ "errors": validation.errors,
+ "warnings": validation.warnings,
+ })
shader = Shader(
author_id=user.id,
@@ -106,23 +175,37 @@ async def create_shader(
glsl_code=body.glsl_code,
tags=body.tags,
shader_type=body.shader_type,
- is_public=body.is_public,
+ is_public=body.is_public if body.status == "published" else False,
+ status=body.status,
style_metadata=body.style_metadata,
- render_status="pending",
+ render_status="ready" if body.status == "draft" else "pending",
+ current_version=1,
)
db.add(shader)
await db.flush()
- # Enqueue render job
- from app.worker import celery_app
- try:
- celery_app.send_task("render_shader", args=[str(shader.id)])
- except Exception:
- # If Celery isn't available (dev without worker), mark as ready
- # with no thumbnail — the frontend can still render live
- shader.render_status = "ready"
+ # Create version 1 snapshot
+ v1 = ShaderVersion(
+ shader_id=shader.id,
+ version_number=1,
+ glsl_code=body.glsl_code,
+ title=body.title,
+ description=body.description,
+ tags=body.tags,
+ style_metadata=body.style_metadata,
+ change_note="Initial version",
+ )
+ db.add(v1)
- # If this shader fulfills a desire, link them
+ # Enqueue render for published shaders
+ if body.status == "published":
+ from app.worker import celery_app
+ try:
+ celery_app.send_task("render_shader", args=[str(shader.id)])
+ except Exception:
+ shader.render_status = "ready"
+
+ # Link to desire if fulfilling
if body.fulfills_desire_id:
from app.models import Desire
desire = (await db.execute(
@@ -136,6 +219,8 @@ async def create_shader(
return shader
+# ── Update shader (creates new version) ──────────────────
+
@router.put("/{shader_id}", response_model=ShaderPublic)
async def update_shader(
shader_id: UUID,
@@ -151,20 +236,40 @@ async def update_shader(
raise HTTPException(status_code=403, detail="Not the shader owner")
updates = body.model_dump(exclude_unset=True)
+ change_note = updates.pop("change_note", None)
+ code_changed = "glsl_code" in updates
# Re-validate GLSL if code changed
- if "glsl_code" in updates:
+ if code_changed:
validation = validate_glsl(updates["glsl_code"], shader.shader_type)
if not validation.valid:
- raise HTTPException(
- status_code=422,
- detail={
- "message": "GLSL validation failed",
- "errors": validation.errors,
- "warnings": validation.warnings,
- }
- )
- # Re-render if code changed
+ raise HTTPException(status_code=422, detail={
+ "message": "GLSL validation failed",
+ "errors": validation.errors,
+ "warnings": validation.warnings,
+ })
+
+ # Apply updates
+ for field, value in updates.items():
+ setattr(shader, field, value)
+
+ # Create a new version snapshot if code or metadata changed
+ if code_changed or "title" in updates or "description" in updates or "tags" in updates:
+ shader.current_version += 1
+ new_version = ShaderVersion(
+ shader_id=shader.id,
+ version_number=shader.current_version,
+ glsl_code=shader.glsl_code,
+ title=shader.title,
+ description=shader.description,
+ tags=shader.tags,
+ style_metadata=shader.style_metadata,
+ change_note=change_note,
+ )
+ db.add(new_version)
+
+ # Re-render if code changed and shader is published
+ if code_changed and shader.status == "published":
shader.render_status = "pending"
from app.worker import celery_app
try:
@@ -172,13 +277,22 @@ async def update_shader(
except Exception:
shader.render_status = "ready"
- for field, value in updates.items():
- setattr(shader, field, value)
+ # If publishing a draft, ensure it's public and queue render
+ if "status" in updates and updates["status"] == "published" and shader.render_status != "ready":
+ shader.is_public = True
+ shader.render_status = "pending"
+ from app.worker import celery_app
+ try:
+ celery_app.send_task("render_shader", args=[str(shader.id)])
+ except Exception:
+ shader.render_status = "ready"
shader.updated_at = datetime.now(timezone.utc)
return shader
+# ── Delete ────────────────────────────────────────────────
+
@router.delete("/{shader_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_shader(
shader_id: UUID,
@@ -191,10 +305,11 @@ async def delete_shader(
raise HTTPException(status_code=404, detail="Shader not found")
if shader.author_id != user.id and user.role != "admin":
raise HTTPException(status_code=403, detail="Not the shader owner")
-
await db.delete(shader)
+# ── Fork ──────────────────────────────────────────────────
+
@router.post("/{shader_id}/fork", response_model=ShaderPublic, status_code=status.HTTP_201_CREATED)
async def fork_shader(
shader_id: UUID,
@@ -205,7 +320,7 @@ async def fork_shader(
original = result.scalar_one_or_none()
if not original:
raise HTTPException(status_code=404, detail="Shader not found")
- if not original.is_public:
+ if not original.is_public and original.status != "published":
raise HTTPException(status_code=404, detail="Shader not found")
forked = Shader(
@@ -217,16 +332,83 @@ async def fork_shader(
shader_type=original.shader_type,
forked_from=original.id,
style_metadata=original.style_metadata,
- render_status="pending",
+ status="draft", # Forks start as drafts
+ is_public=False,
+ render_status="ready",
+ current_version=1,
)
db.add(forked)
await db.flush()
- # Enqueue render for the fork
- from app.worker import celery_app
- try:
- celery_app.send_task("render_shader", args=[str(forked.id)])
- except Exception:
- forked.render_status = "ready"
+ v1 = ShaderVersion(
+ shader_id=forked.id,
+ version_number=1,
+ glsl_code=original.glsl_code,
+ title=forked.title,
+ description=forked.description,
+ tags=original.tags,
+ style_metadata=original.style_metadata,
+ change_note=f"Forked from {original.title}",
+ )
+ db.add(v1)
return forked
+
+
+# ── Restore a version ────────────────────────────────────
+
+@router.post("/{shader_id}/versions/{version_number}/restore", response_model=ShaderPublic)
+async def restore_version(
+ shader_id: UUID,
+ version_number: int,
+ db: AsyncSession = Depends(get_db),
+ user: User = Depends(get_current_user),
+):
+ """Restore a shader to a previous version (creates a new version snapshot)."""
+ shader = (await db.execute(select(Shader).where(Shader.id == shader_id))).scalar_one_or_none()
+ if not shader:
+ raise HTTPException(status_code=404, detail="Shader not found")
+ if shader.author_id != user.id and user.role != "admin":
+ raise HTTPException(status_code=403, detail="Not the shader owner")
+
+ version = (await db.execute(
+ select(ShaderVersion).where(
+ ShaderVersion.shader_id == shader_id,
+ ShaderVersion.version_number == version_number,
+ )
+ )).scalar_one_or_none()
+ if not version:
+ raise HTTPException(status_code=404, detail="Version not found")
+
+ # Apply version data to shader
+ shader.glsl_code = version.glsl_code
+ shader.title = version.title
+ shader.description = version.description
+ shader.tags = version.tags
+ shader.style_metadata = version.style_metadata
+ shader.current_version += 1
+ shader.updated_at = datetime.now(timezone.utc)
+
+ # Create a new version snapshot for the restore
+ restore_v = ShaderVersion(
+ shader_id=shader.id,
+ version_number=shader.current_version,
+ glsl_code=version.glsl_code,
+ title=version.title,
+ description=version.description,
+ tags=version.tags,
+ style_metadata=version.style_metadata,
+ change_note=f"Restored from version {version_number}",
+ )
+ db.add(restore_v)
+
+ # Re-render if published
+ if shader.status == "published":
+ shader.render_status = "pending"
+ from app.worker import celery_app
+ try:
+ celery_app.send_task("render_shader", args=[str(shader.id)])
+ except Exception:
+ shader.render_status = "ready"
+
+ return shader
diff --git a/services/api/app/schemas/schemas.py b/services/api/app/schemas/schemas.py
index e2de2bb..7a66ee9 100644
--- a/services/api/app/schemas/schemas.py
+++ b/services/api/app/schemas/schemas.py
@@ -35,6 +35,7 @@ class UserPublic(BaseModel):
id: UUID
username: str
role: str
+ is_system: bool
subscription_tier: str
is_verified_creator: bool
created_at: datetime
@@ -53,7 +54,6 @@ class UserUpdate(BaseModel):
class ByokKeysUpdate(BaseModel):
- """Bring Your Own Key — encrypted API keys for AI providers."""
anthropic_key: Optional[str] = Field(None, description="Anthropic API key")
openai_key: Optional[str] = Field(None, description="OpenAI API key")
ollama_endpoint: Optional[str] = Field(None, description="Ollama endpoint URL")
@@ -70,6 +70,7 @@ class ShaderCreate(BaseModel):
tags: list[str] = Field(default_factory=list, max_length=10)
shader_type: str = Field(default="2d", pattern=r"^(2d|3d|audio-reactive)$")
is_public: bool = True
+ status: str = Field(default="published", pattern=r"^(draft|published)$")
style_metadata: Optional[dict] = None
fulfills_desire_id: Optional[UUID] = None
@@ -80,6 +81,8 @@ class ShaderUpdate(BaseModel):
glsl_code: Optional[str] = Field(None, min_length=10)
tags: Optional[list[str]] = None
is_public: Optional[bool] = None
+ status: Optional[str] = Field(None, pattern=r"^(draft|published|archived)$")
+ change_note: Optional[str] = Field(None, max_length=200)
class ShaderPublic(BaseModel):
@@ -90,9 +93,12 @@ class ShaderPublic(BaseModel):
title: str
description: Optional[str]
glsl_code: str
+ status: str
is_public: bool
is_ai_generated: bool
+ is_system: bool
ai_provider: Optional[str]
+ system_label: Optional[str]
thumbnail_url: Optional[str]
preview_url: Optional[str]
render_status: str
@@ -100,6 +106,7 @@ class ShaderPublic(BaseModel):
tags: list[str]
shader_type: str
forked_from: Optional[UUID]
+ current_version: int
view_count: int
score: float
created_at: datetime
@@ -121,10 +128,28 @@ class ShaderFeedItem(BaseModel):
score: float
view_count: int
is_ai_generated: bool
+ is_system: bool
+ system_label: Optional[str]
style_metadata: Optional[dict]
created_at: datetime
+class ShaderVersionPublic(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+
+ id: UUID
+ shader_id: UUID
+ version_number: int
+ glsl_code: str
+ title: str
+ description: Optional[str]
+ tags: list[str]
+ style_metadata: Optional[dict]
+ change_note: Optional[str]
+ thumbnail_url: Optional[str]
+ created_at: datetime
+
+
# ════════════════════════════════════════════════════════════
# VOTES & ENGAGEMENT
# ════════════════════════════════════════════════════════════
@@ -170,13 +195,13 @@ class DesirePublic(BaseModel):
class GenerateRequest(BaseModel):
prompt: str = Field(..., min_length=5, max_length=500)
- provider: Optional[str] = None # anthropic, openai, ollama — auto-selected if None
+ provider: Optional[str] = None
style_metadata: Optional[dict] = None
class GenerateStatusResponse(BaseModel):
job_id: str
- status: str # queued, generating, rendering, complete, failed
+ status: str
shader_id: Optional[UUID] = None
error: Optional[str] = None
@@ -202,7 +227,6 @@ class ApiKeyPublic(BaseModel):
class ApiKeyCreated(ApiKeyPublic):
- """Returned only on creation — includes the full key (shown once)."""
full_key: str
diff --git a/services/frontend/src/App.tsx b/services/frontend/src/App.tsx
index 83871a7..e48b3d3 100644
--- a/services/frontend/src/App.tsx
+++ b/services/frontend/src/App.tsx
@@ -4,6 +4,7 @@ import Feed from './pages/Feed';
import Explore from './pages/Explore';
import ShaderDetail from './pages/ShaderDetail';
import Editor from './pages/Editor';
+import MyShaders from './pages/MyShaders';
import Generate from './pages/Generate';
import Bounties from './pages/Bounties';
import BountyDetail from './pages/BountyDetail';
@@ -21,6 +22,7 @@ export default function App() {
+ Your workspace — drafts, published shaders, and version history. +
+No {tab === 'all' ? '' : tab + ' '}shaders yet
+ Create Your First Shader +