Initial commit

This commit is contained in:
Allen
2026-04-30 02:14:25 +00:00
commit 7090e6f01d
243 changed files with 115122 additions and 0 deletions
@@ -0,0 +1,396 @@
// abstract.js — Abstract theme backgrounds
// Variants: geometricPatterns, gradients, particleFields, minimalist, waves, fractals, kaleidoscope, matrix
(function(engine) {
if (!engine) return;
var D = engine.Draw, H = engine.helpers;
engine.registerTheme('abstract', {
geometricPatterns: {
meta: { id: 'abstract_geometricPatterns', mood: 'focused', colors: ['#0d1b2a', '#1b2838', '#e07a5f', '#f2cc8f'], tags: ['geometric', 'shapes', 'patterns', 'clean'], description: 'Interlocking geometric shapes in warm contrasting tones on a deep navy canvas', compatibleMusicMoods: ['focused', 'calm', 'electronic'], recommendedFor: ['puzzle', 'trivia', 'creative'] },
colors: ['#0d1b2a', '#1b2838', '#e07a5f', '#f2cc8f'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 135);
},
renderMid: function(ctx, w, h, c, Draw, t) {
var rng = H.seededRandom(100);
// Rotating hexagonal grid
var spacing = 80;
for (var row = -1; row < h / spacing + 1; row++) {
for (var col = -1; col < w / spacing + 1; col++) {
var hx = col * spacing + (row % 2 === 0 ? spacing / 2 : 0);
var hy = row * spacing;
var dist = Math.sqrt(Math.pow(hx - w / 2, 2) + Math.pow(hy - h / 2, 2));
var alpha = H.clamp(0.08 + Math.sin(t * 0.5 + dist * 0.005) * 0.06, 0, 0.2);
var rot = t * 0.1 + dist * 0.002;
var colorChoice = rng() > 0.5 ? c[2] : c[3];
Draw.polygon(ctx, hx, hy, 25 + Math.sin(t * 0.3 + dist * 0.01) * 5, 6, H.rgba(colorChoice, alpha), rot);
}
}
// Accent triangles
for (var tri = 0; tri < 12; tri++) {
var tx = rng() * w, ty = rng() * h;
var tSize = 20 + rng() * 40;
var tRot = t * 0.2 * (rng() > 0.5 ? 1 : -1) + rng() * Math.PI * 2;
var tAlpha = 0.1 + Math.sin(t * 0.7 + tri) * 0.05;
Draw.polygon(ctx, tx, ty, tSize, 3, H.rgba(c[2], tAlpha), tRot);
}
// Connecting lines
ctx.strokeStyle = H.rgba(c[3], 0.06);
ctx.lineWidth = 1;
var lrng = H.seededRandom(101);
for (var ln = 0; ln < 15; ln++) {
ctx.beginPath();
ctx.moveTo(lrng() * w, lrng() * h);
ctx.lineTo(lrng() * w, lrng() * h);
ctx.stroke();
}
},
particles: { type: 'dust', count: 20, color: '#F2CC8F' }
},
gradients: {
meta: { id: 'abstract_gradients', mood: 'dreamy', colors: ['#0f0c29', '#302b63', '#24243e', '#ff6b6b'], tags: ['gradient', 'smooth', 'colorful', 'flowing'], description: 'Smoothly blending gradient orbs that shift and pulse with ethereal color transitions', compatibleMusicMoods: ['dreamy', 'calm', 'ambient'], recommendedFor: ['creative', 'story', 'music'] },
colors: ['#0f0c29', '#302b63', '#24243e', '#ff6b6b'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1], c[2]], 90);
},
renderMid: function(ctx, w, h, c, Draw, t) {
var rng = H.seededRandom(200);
// Animated gradient orbs
var orbs = [
{ x: 0.3, y: 0.3, color: c[3], r: 0.25 },
{ x: 0.7, y: 0.6, color: '#6b5bff', r: 0.3 },
{ x: 0.5, y: 0.8, color: '#ff9a76', r: 0.2 },
{ x: 0.2, y: 0.7, color: '#a66bff', r: 0.22 },
{ x: 0.8, y: 0.2, color: '#ff6bb5', r: 0.18 }
];
ctx.globalCompositeOperation = 'screen';
for (var i = 0; i < orbs.length; i++) {
var orb = orbs[i];
var ox = orb.x * w + Math.sin(t * 0.3 + i * 1.5) * w * 0.05;
var oy = orb.y * h + Math.cos(t * 0.2 + i * 1.2) * h * 0.05;
var or = orb.r * Math.min(w, h) * (1 + Math.sin(t * 0.4 + i) * 0.1);
var grad = Draw.radialGradient(ctx, ox, oy, or, [
[0, H.rgba(orb.color, 0.3)],
[0.5, H.rgba(orb.color, 0.1)],
[1, H.rgba(orb.color, 0)]
]);
ctx.fillStyle = grad;
ctx.beginPath(); ctx.arc(ox, oy, or, 0, Math.PI * 2); ctx.fill();
}
ctx.globalCompositeOperation = 'source-over';
// Subtle mesh lines
ctx.strokeStyle = H.rgba('#FFFFFF', 0.02);
ctx.lineWidth = 1;
for (var ml = 0; ml < 8; ml++) {
var my = h * 0.1 + ml * h * 0.1;
ctx.beginPath();
for (var mx = 0; mx < w; mx += 5) {
var mdy = my + Math.sin(mx * 0.01 + t * 0.5 + ml * 0.5) * 15;
mx === 0 ? ctx.moveTo(mx, mdy) : ctx.lineTo(mx, mdy);
}
ctx.stroke();
}
},
particles: { type: 'sparkles', count: 15, color: '#FF6B6B' }
},
particleFields: {
meta: { id: 'abstract_particleFields', mood: 'energetic', colors: ['#000000', '#111122', '#00ff88', '#0088ff'], tags: ['particles', 'field', 'connected', 'network'], description: 'A dynamic network of connected particle nodes with pulsing energy links', compatibleMusicMoods: ['energetic', 'electronic', 'focused'], recommendedFor: ['action', 'puzzle', 'physics'] },
colors: ['#000000', '#111122', '#00ff88', '#0088ff'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
},
renderMid: function(ctx, w, h, c, Draw, t) {
var rng = H.seededRandom(300);
// Generate node positions (deterministic)
var nodes = [];
for (var n = 0; n < 30; n++) {
var baseX = rng() * w, baseY = rng() * h;
nodes.push({
x: baseX + Math.sin(t * 0.5 + n * 0.7) * 15,
y: baseY + Math.cos(t * 0.4 + n * 0.9) * 15,
size: 2 + rng() * 3,
color: rng() > 0.5 ? c[2] : c[3]
});
}
// Draw connections between nearby nodes
var maxDist = 150;
for (var i = 0; i < nodes.length; i++) {
for (var j = i + 1; j < nodes.length; j++) {
var dx = nodes[i].x - nodes[j].x;
var dy = nodes[i].y - nodes[j].y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < maxDist) {
var lineAlpha = (1 - dist / maxDist) * 0.2;
var pulse = 0.5 + Math.sin(t * 1.5 + i + j) * 0.5;
ctx.strokeStyle = H.rgba(nodes[i].color, lineAlpha * pulse);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(nodes[i].x, nodes[i].y);
ctx.lineTo(nodes[j].x, nodes[j].y);
ctx.stroke();
}
}
}
// Draw nodes
for (var k = 0; k < nodes.length; k++) {
var nd = nodes[k];
var nodePulse = 0.6 + Math.sin(t * 2 + k * 0.5) * 0.3;
Draw.glow(ctx, nd.x, nd.y, nd.size * 4, H.rgba(nd.color, 0.15 * nodePulse));
Draw.circle(ctx, nd.x, nd.y, nd.size, H.rgba(nd.color, 0.7 * nodePulse));
}
// Data stream accents along bottom
ctx.strokeStyle = H.rgba(c[2], 0.04);
ctx.lineWidth = 1;
for (var ds = 0; ds < 5; ds++) {
var dsy = h * 0.85 + ds * 8;
ctx.beginPath();
for (var dx2 = 0; dx2 < w; dx2 += 3) {
var ddy = dsy + Math.sin(dx2 * 0.03 + t * 2 + ds) * 3;
dx2 === 0 ? ctx.moveTo(dx2, ddy) : ctx.lineTo(dx2, ddy);
}
ctx.stroke();
}
},
particles: { type: 'particles', count: 15, color: '#00FF88' }
},
minimalist: {
meta: { id: 'abstract_minimalist', mood: 'calm', colors: ['#f5f5f0', '#e8e4dd', '#2d2d2d', '#c44536'], tags: ['minimal', 'clean', 'simple', 'elegant'], description: 'Clean open space with a single bold accent line and carefully placed geometric forms', compatibleMusicMoods: ['calm', 'ambient', 'focused'], recommendedFor: ['puzzle', 'trivia', 'creative'] },
colors: ['#f5f5f0', '#e8e4dd', '#2d2d2d', '#c44536'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Single horizon line
ctx.strokeStyle = H.rgba(c[2], 0.15);
ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(0, h * 0.618); ctx.lineTo(w, h * 0.618); ctx.stroke();
// Accent circle
var circX = w * 0.7, circY = h * 0.35;
var circR = Math.min(w, h) * 0.08;
var breathe = 1 + Math.sin(t * 0.5) * 0.03;
Draw.circle(ctx, circX, circY, circR * breathe, H.rgba(c[3], 0.7));
// Shadow beneath circle
ctx.fillStyle = H.rgba(c[2], 0.04);
ctx.beginPath(); ctx.ellipse(circX, h * 0.618, circR * 1.5, 4, 0, 0, Math.PI * 2); ctx.fill();
// Thin accent line from circle
ctx.strokeStyle = H.rgba(c[3], 0.2);
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(circX, circY + circR * breathe);
ctx.lineTo(circX, h * 0.618);
ctx.stroke();
// Sparse dots
var rng = H.seededRandom(400);
for (var d = 0; d < 5; d++) {
var dx = rng() * w, dy = rng() * h;
Draw.circle(ctx, dx, dy, 2, H.rgba(c[2], 0.08));
}
// Small typography-like lines
var lx = w * 0.15;
for (var l = 0; l < 4; l++) {
var lw = 30 + rng() * 60;
ctx.fillStyle = H.rgba(c[2], 0.06);
ctx.fillRect(lx, h * 0.72 + l * 12, lw, 2);
}
},
particles: null
},
waves: {
meta: { id: 'abstract_waves', mood: 'flowing', colors: ['#0a192f', '#172a45', '#00b4d8', '#90e0ef'], tags: ['waves', 'ocean', 'flowing', 'smooth'], description: 'Layered sinusoidal waves undulating in cool blue tones with foam accents', compatibleMusicMoods: ['calm', 'flowing', 'ambient'], recommendedFor: ['creative', 'music', 'physics'] },
colors: ['#0a192f', '#172a45', '#00b4d8', '#90e0ef'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Layered wave bands
var waveLayers = [
{ y: 0.3, amp: 25, freq: 0.008, speed: 0.4, color: c[2], alpha: 0.08 },
{ y: 0.4, amp: 30, freq: 0.006, speed: 0.3, color: c[2], alpha: 0.1 },
{ y: 0.5, amp: 20, freq: 0.01, speed: 0.5, color: c[3], alpha: 0.12 },
{ y: 0.6, amp: 35, freq: 0.005, speed: 0.25, color: c[2], alpha: 0.15 },
{ y: 0.7, amp: 15, freq: 0.012, speed: 0.6, color: c[3], alpha: 0.1 },
{ y: 0.8, amp: 40, freq: 0.004, speed: 0.2, color: c[2], alpha: 0.18 }
];
for (var i = 0; i < waveLayers.length; i++) {
var wl = waveLayers[i];
ctx.fillStyle = H.rgba(wl.color, wl.alpha);
ctx.beginPath();
ctx.moveTo(0, h);
for (var x = 0; x <= w; x += 3) {
var y = wl.y * h + Math.sin(x * wl.freq + t * wl.speed) * wl.amp +
Math.sin(x * wl.freq * 2.3 + t * wl.speed * 1.5) * wl.amp * 0.3;
ctx.lineTo(x, y);
}
ctx.lineTo(w, h);
ctx.closePath();
ctx.fill();
}
// Foam highlights on top wave
ctx.strokeStyle = H.rgba('#FFFFFF', 0.06);
ctx.lineWidth = 1.5;
ctx.beginPath();
for (var fx = 0; fx <= w; fx += 3) {
var fy = 0.3 * h + Math.sin(fx * 0.008 + t * 0.4) * 25;
fx === 0 ? ctx.moveTo(fx, fy) : ctx.lineTo(fx, fy);
}
ctx.stroke();
},
particles: { type: 'bubbles', count: 15, color: '#90E0EF' }
},
fractals: {
meta: { id: 'abstract_fractals', mood: 'complex', colors: ['#0a0a0a', '#1a1a2e', '#e94560', '#0f3460'], tags: ['fractal', 'recursive', 'mathematical', 'complex'], description: 'Recursive branching patterns that grow and pulse like living mathematical organisms', compatibleMusicMoods: ['complex', 'electronic', 'mystical'], recommendedFor: ['puzzle', 'physics', 'creative'] },
colors: ['#0a0a0a', '#1a1a2e', '#e94560', '#0f3460'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Recursive tree fractal
function branch(x, y, len, angle, depth, maxDepth) {
if (depth > maxDepth || len < 3) return;
var endX = x + Math.cos(angle) * len;
var endY = y + Math.sin(angle) * len;
var alpha = 0.15 * (1 - depth / maxDepth) + Math.sin(t * 0.5 + depth) * 0.03;
var lw = (maxDepth - depth) * 0.8 + 0.5;
ctx.strokeStyle = H.rgba(depth % 2 === 0 ? c[2] : c[3], alpha);
ctx.lineWidth = lw;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(endX, endY); ctx.stroke();
var spread = 0.4 + Math.sin(t * 0.3 + depth * 0.5) * 0.1;
branch(endX, endY, len * 0.7, angle - spread, depth + 1, maxDepth);
branch(endX, endY, len * 0.7, angle + spread, depth + 1, maxDepth);
}
// Main tree from bottom center
branch(w * 0.5, h * 0.85, h * 0.18, -Math.PI / 2, 0, 9);
// Smaller trees
branch(w * 0.2, h * 0.9, h * 0.1, -Math.PI / 2, 0, 7);
branch(w * 0.8, h * 0.9, h * 0.12, -Math.PI / 2, 0, 7);
// Spiral accents
var rng = H.seededRandom(500);
for (var sp = 0; sp < 4; sp++) {
var sx = rng() * w, sy = rng() * h * 0.6;
ctx.strokeStyle = H.rgba(c[2], 0.06);
ctx.lineWidth = 1;
ctx.beginPath();
for (var sa = 0; sa < Math.PI * 6; sa += 0.1) {
var sr = sa * 2 + Math.sin(t * 0.3 + sp) * 2;
var ssx = sx + Math.cos(sa) * sr;
var ssy = sy + Math.sin(sa) * sr;
sa === 0 ? ctx.moveTo(ssx, ssy) : ctx.lineTo(ssx, ssy);
}
ctx.stroke();
}
},
particles: { type: 'sparkles', count: 20, color: '#E94560' }
},
kaleidoscope: {
meta: { id: 'abstract_kaleidoscope', mood: 'playful', colors: ['#1a1a2e', '#16213e', '#e94560', '#ffd166'], tags: ['kaleidoscope', 'symmetry', 'colorful', 'playful'], description: 'Symmetric radial patterns in vibrant colors rotating like a living kaleidoscope', compatibleMusicMoods: ['playful', 'energetic', 'electronic'], recommendedFor: ['music', 'creative', 'puzzle'] },
colors: ['#1a1a2e', '#16213e', '#e94560', '#ffd166'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
},
renderMid: function(ctx, w, h, c, Draw, t) {
var cx = w / 2, cy = h / 2;
var segments = 8;
var rng = H.seededRandom(600);
ctx.save();
ctx.translate(cx, cy);
// Draw mirrored segments
for (var seg = 0; seg < segments; seg++) {
var segAngle = (seg / segments) * Math.PI * 2 + t * 0.1;
ctx.save();
ctx.rotate(segAngle);
// Shapes within each segment
for (var s = 0; s < 5; s++) {
var sd = 40 + s * 35 + Math.sin(t * 0.4 + s) * 10;
var sSize = 8 + rng() * 15;
var sColor = s % 2 === 0 ? c[2] : c[3];
var sAlpha = 0.12 + Math.sin(t * 0.6 + s * 0.8) * 0.05;
Draw.polygon(ctx, sd, 0, sSize, 3 + (s % 3), H.rgba(sColor, sAlpha), t * 0.2 + s);
}
// Connecting arcs
ctx.strokeStyle = H.rgba(c[2], 0.06);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(0, 0, 80 + Math.sin(t * 0.5) * 10, 0, Math.PI / segments);
ctx.stroke();
ctx.restore();
}
ctx.restore();
// Central glow
var centerPulse = 0.15 + Math.sin(t * 0.8) * 0.05;
Draw.glow(ctx, cx, cy, 60, H.rgba(c[3], centerPulse));
Draw.glow(ctx, cx, cy, 30, H.rgba(c[2], centerPulse * 1.5));
// Outer ring
ctx.strokeStyle = H.rgba(c[3], 0.08 + Math.sin(t * 0.3) * 0.03);
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(cx, cy, Math.min(w, h) * 0.35, 0, Math.PI * 2);
ctx.stroke();
},
particles: { type: 'sparkles', count: 25, color: '#FFD166' }
},
matrix: {
meta: { id: 'abstract_matrix', mood: 'digital', colors: ['#000000', '#001100', '#00ff41', '#008f11'], tags: ['matrix', 'code', 'digital', 'hacker'], description: 'Cascading columns of digital characters streaming down like the matrix rain', compatibleMusicMoods: ['dark', 'electronic', 'tense'], recommendedFor: ['action', 'puzzle', 'trivia'] },
colors: ['#000000', '#001100', '#00ff41', '#008f11'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
// CRT vignette
var vignette = Draw.radialGradient(ctx, w / 2, h / 2, Math.max(w, h) * 0.7, [
[0, H.rgba(c[1], 0)],
[0.7, H.rgba(c[0], 0.3)],
[1, H.rgba(c[0], 0.8)]
]);
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, w, h);
},
renderMid: function(ctx, w, h, c, Draw, t) {
var rng = H.seededRandom(700);
var columns = Math.floor(w / 16);
var chars = '01アイウエオカキクケコサシスセソ{}[]<>+=/*#@$%&';
ctx.font = '13px monospace';
// Column rain
for (var col = 0; col < columns; col++) {
var cx = col * 16 + 4;
var speed = 0.3 + rng() * 0.7;
var colOffset = rng() * h;
var colLen = 8 + Math.floor(rng() * 20);
// Calculate head position
var headY = ((t * speed * 60 + colOffset) % (h + colLen * 16)) - colLen * 16;
for (var ch = 0; ch < colLen; ch++) {
var cy = headY + ch * 16;
if (cy < -16 || cy > h + 16) continue;
var charIdx = Math.floor(rng() * chars.length);
// Head is brightest, tail fades
var fade = 1 - (ch / colLen);
if (ch === 0) {
ctx.fillStyle = H.rgba('#FFFFFF', 0.8);
} else {
ctx.fillStyle = H.rgba(c[2], fade * 0.5 + Math.sin(t * 3 + col + ch) * 0.08);
}
ctx.fillText(chars[charIdx], cx, cy);
}
}
// Occasional bright flashes
if (Math.sin(t * 1.3) > 0.95) {
var flashX = Math.floor(rng() * columns) * 16;
var flashY = rng() * h;
Draw.glow(ctx, flashX, flashY, 30, H.rgba(c[2], 0.3));
}
// Subtle horizontal scan line
var scanY = (t * 40) % h;
ctx.fillStyle = H.rgba(c[2], 0.03);
ctx.fillRect(0, scanY, w, 2);
},
particles: null
}
});
})(window.BackgroundEngine);