Initial commit
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
// space.js — Space theme for procedural background system
|
||||
// 8 variants: nebula, asteroidField, deepSpace, planets, spaceStation, warpSpeed, satellites, blackHole
|
||||
|
||||
(function(engine) {
|
||||
if (!engine) return;
|
||||
var D = engine.Draw, H = engine.helpers;
|
||||
|
||||
engine.registerTheme('space', {
|
||||
|
||||
nebula: {
|
||||
meta: { id: 'space_nebula', mood: 'wonder', colors: ['#0B0B2B', '#1A0A3E', '#3D1A78', '#7B2FBE'], tags: ['cosmic', 'colorful', 'calm'], description: 'Swirling nebula clouds with twinkling stars', compatibleMusicMoods: ['ambient', 'dreamy', 'epic'], recommendedFor: ['puzzle', 'story', 'creative'] },
|
||||
colors: ['#0B0B2B', '#1A0A3E', '#3D1A78', '#7B2FBE'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[1], c[2]], 90);
|
||||
var rng = H.seededRandom(101);
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var cx = rng() * w, cy = rng() * h;
|
||||
var r = 80 + rng() * 200;
|
||||
var pulse = 0.9 + Math.sin(t * 0.3 + i) * 0.1;
|
||||
Draw.glow(ctx, cx, cy, r * pulse, c[3]);
|
||||
Draw.glow(ctx, cx + 40, cy - 30, r * 0.6 * pulse, c[2]);
|
||||
}
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.stars(ctx, w, h, 180, '#FFFFFF', 10, [0.5, 2]);
|
||||
Draw.stars(ctx, w, h, 40, c[3], 20, [1, 3]);
|
||||
var rng = H.seededRandom(102);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var x = rng() * w, y = rng() * h * 0.8;
|
||||
var flicker = 0.5 + Math.sin(t * 2 + i * 1.7) * 0.3;
|
||||
Draw.glow(ctx, x, y, 4 + rng() * 4, H.rgba('#FFFFFF', flicker));
|
||||
}
|
||||
},
|
||||
particles: { type: 'sparkles', count: 40, color: '#CCBBFF' }
|
||||
},
|
||||
|
||||
asteroidField: {
|
||||
meta: { id: 'space_asteroidField', mood: 'tense', colors: ['#0A0A14', '#1C1C2E', '#4A3A2A', '#8B7355'], tags: ['rocky', 'danger', 'dark'], description: 'Rocky asteroids drifting through dark space', compatibleMusicMoods: ['tense', 'action', 'ambient'], recommendedFor: ['action', 'racing', 'sports'] },
|
||||
colors: ['#0A0A14', '#1C1C2E', '#4A3A2A', '#8B7355'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
|
||||
Draw.stars(ctx, w, h, 100, '#FFFFFF', 30, [0.3, 1.5]);
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
var rng = H.seededRandom(200);
|
||||
for (var i = 0; i < 18; i++) {
|
||||
var ax = (rng() * w + t * (5 + rng() * 15)) % (w + 100) - 50;
|
||||
var ay = rng() * h;
|
||||
var ar = 8 + rng() * 35;
|
||||
var sides = 5 + Math.floor(rng() * 4);
|
||||
var rot = rng() * Math.PI * 2 + t * (rng() - 0.5) * 0.2;
|
||||
var shade = H.blendColor(c[2], c[3], rng());
|
||||
Draw.polygon(ctx, ax, ay, ar, sides, shade, rot);
|
||||
Draw.polygon(ctx, ax - ar * 0.15, ay - ar * 0.15, ar * 0.85, sides, H.lighten(shade, 0.15), rot);
|
||||
}
|
||||
},
|
||||
particles: { type: 'dust', count: 25, color: '#887766' }
|
||||
},
|
||||
|
||||
deepSpace: {
|
||||
meta: { id: 'space_deepSpace', mood: 'serene', colors: ['#000005', '#050510', '#0A0A20', '#111133'], tags: ['dark', 'vast', 'minimal'], description: 'The void of deep space with distant starlight', compatibleMusicMoods: ['ambient', 'calm', 'dreamy'], recommendedFor: ['puzzle', 'story', 'rpg'] },
|
||||
colors: ['#000005', '#050510', '#0A0A20', '#111133'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[1], c[2]], 90);
|
||||
Draw.glow(ctx, w * 0.8, h * 0.15, 150, H.rgba('#1A1A55', 0.3));
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.stars(ctx, w, h, 300, '#FFFFFF', 50, [0.2, 1.5]);
|
||||
Draw.stars(ctx, w, h, 20, '#AABBFF', 51, [1.5, 3]);
|
||||
var rng = H.seededRandom(300);
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var x = rng() * w, y = rng() * h;
|
||||
var twinkle = 0.3 + Math.sin(t * 1.5 + rng() * 10) * 0.3;
|
||||
Draw.glow(ctx, x, y, 8 + rng() * 12, H.rgba('#4466CC', twinkle));
|
||||
}
|
||||
},
|
||||
particles: { type: 'stars', count: 60, color: '#FFFFFF' }
|
||||
},
|
||||
|
||||
planets: {
|
||||
meta: { id: 'space_planets', mood: 'wonder', colors: ['#0B0B2B', '#0F1640', '#CC5533', '#44BBAA'], tags: ['planetary', 'colorful', 'majestic'], description: 'Colorful planets against a starry backdrop', compatibleMusicMoods: ['epic', 'dreamy', 'ambient'], recommendedFor: ['creative', 'rpg', 'story'] },
|
||||
colors: ['#0B0B2B', '#0F1640', '#CC5533', '#44BBAA'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
|
||||
Draw.stars(ctx, w, h, 150, '#FFFFFF', 40, [0.3, 1.8]);
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
var rng = H.seededRandom(400);
|
||||
// Large planet
|
||||
var px = w * 0.7, py = h * 0.35, pr = 60 + rng() * 40;
|
||||
var g = Draw.radialGradient(ctx, px - pr * 0.3, py - pr * 0.3, pr * 1.2, [[0, H.lighten(c[2], 0.3)], [0.6, c[2]], [1, H.darken(c[2], 0.6)]]);
|
||||
ctx.fillStyle = g;
|
||||
ctx.beginPath(); ctx.arc(px, py, pr, 0, Math.PI * 2); ctx.fill();
|
||||
// Ring
|
||||
ctx.strokeStyle = H.rgba(c[2], 0.4);
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath(); ctx.ellipse(px, py, pr * 1.8, pr * 0.3, -0.2, 0, Math.PI * 2); ctx.stroke();
|
||||
// Small planet
|
||||
var sx = w * 0.25, sy = h * 0.6, sr = 25 + rng() * 15;
|
||||
var g2 = Draw.radialGradient(ctx, sx - sr * 0.3, sy - sr * 0.3, sr * 1.2, [[0, H.lighten(c[3], 0.3)], [0.7, c[3]], [1, H.darken(c[3], 0.5)]]);
|
||||
ctx.fillStyle = g2;
|
||||
ctx.beginPath(); ctx.arc(sx, sy, sr, 0, Math.PI * 2); ctx.fill();
|
||||
// Moon
|
||||
var bob = Math.sin(t * 0.5) * 5;
|
||||
var mx = w * 0.15, my = h * 0.25 + bob;
|
||||
Draw.circle(ctx, mx, my, 10, '#AAAAAA');
|
||||
Draw.glow(ctx, mx, my, 18, H.rgba('#AAAAAA', 0.2));
|
||||
},
|
||||
particles: { type: 'sparkles', count: 30, color: '#FFFFFF' }
|
||||
},
|
||||
|
||||
spaceStation: {
|
||||
meta: { id: 'space_spaceStation', mood: 'focused', colors: ['#0A0A1E', '#151530', '#3A3A55', '#88AACC'], tags: ['tech', 'structure', 'industrial'], description: 'Orbital station with geometric structures', compatibleMusicMoods: ['ambient', 'tense', 'electronic'], recommendedFor: ['puzzle', 'action', 'physics'] },
|
||||
colors: ['#0A0A1E', '#151530', '#3A3A55', '#88AACC'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
|
||||
Draw.stars(ctx, w, h, 100, '#FFFFFF', 60, [0.3, 1.2]);
|
||||
// Earth glow on horizon
|
||||
Draw.glow(ctx, w * 0.5, h * 1.1, h * 0.5, H.rgba('#2255AA', 0.25));
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
var rng = H.seededRandom(500);
|
||||
// Station core
|
||||
Draw.rect(ctx, w * 0.35, h * 0.38, w * 0.3, h * 0.06, c[2]);
|
||||
Draw.rect(ctx, w * 0.33, h * 0.4, w * 0.34, h * 0.02, H.lighten(c[2], 0.1));
|
||||
// Solar panels
|
||||
for (var s = 0; s < 2; s++) {
|
||||
var sx = s === 0 ? w * 0.15 : w * 0.7;
|
||||
Draw.rect(ctx, sx, h * 0.36, w * 0.14, h * 0.1, H.rgba(c[3], 0.5));
|
||||
Draw.grid(ctx, w * 0.14, h * 0.1, 10, H.rgba(c[2], 0.3), 0.5);
|
||||
ctx.save(); ctx.translate(sx, h * 0.36);
|
||||
Draw.grid(ctx, w * 0.14, h * 0.1, 12, H.rgba('#4466AA', 0.3), 0.5);
|
||||
ctx.restore();
|
||||
}
|
||||
// Struts
|
||||
Draw.rect(ctx, w * 0.29, h * 0.4, w * 0.04, h * 0.005, c[2]);
|
||||
Draw.rect(ctx, w * 0.67, h * 0.4, w * 0.04, h * 0.005, c[2]);
|
||||
// Blinking lights
|
||||
for (var i = 0; i < 6; i++) {
|
||||
var lx = w * 0.36 + i * (w * 0.28 / 5);
|
||||
var blink = Math.sin(t * 3 + i * 1.2) > 0.3 ? 0.9 : 0.15;
|
||||
Draw.circle(ctx, lx, h * 0.38, 2, H.rgba('#FF3333', blink));
|
||||
}
|
||||
},
|
||||
particles: { type: 'dust', count: 15, color: '#667788' }
|
||||
},
|
||||
|
||||
warpSpeed: {
|
||||
meta: { id: 'space_warpSpeed', mood: 'energetic', colors: ['#000010', '#0A0A30', '#2244AA', '#66BBFF'], tags: ['fast', 'streaks', 'intense'], description: 'Light streaks from faster-than-light travel', compatibleMusicMoods: ['energetic', 'electronic', 'epic'], recommendedFor: ['racing', 'action', 'sports'] },
|
||||
colors: ['#000010', '#0A0A30', '#2244AA', '#66BBFF'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
|
||||
// Central tunnel glow
|
||||
var pulse = 0.6 + Math.sin(t * 2) * 0.15;
|
||||
Draw.glow(ctx, w * 0.5, h * 0.5, Math.min(w, h) * 0.4 * pulse, c[2]);
|
||||
Draw.glow(ctx, w * 0.5, h * 0.5, Math.min(w, h) * 0.15, H.rgba(c[3], 0.4));
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
var rng = H.seededRandom(600);
|
||||
var cx = w * 0.5, cy = h * 0.5;
|
||||
ctx.lineWidth = 1.5;
|
||||
for (var i = 0; i < 60; i++) {
|
||||
var angle = rng() * Math.PI * 2;
|
||||
var dist = 30 + rng() * Math.min(w, h) * 0.45;
|
||||
var len = 20 + rng() * 60;
|
||||
var speed = 0.5 + rng() * 2;
|
||||
var offset = (t * speed * 40 + rng() * 200) % (dist + len);
|
||||
var x1 = cx + Math.cos(angle) * offset;
|
||||
var y1 = cy + Math.sin(angle) * offset;
|
||||
var x2 = cx + Math.cos(angle) * (offset + len);
|
||||
var y2 = cy + Math.sin(angle) * (offset + len);
|
||||
var alpha = H.clamp(0.15 + (offset / dist) * 0.6, 0, 0.8);
|
||||
var col = rng() > 0.7 ? c[3] : '#FFFFFF';
|
||||
ctx.strokeStyle = H.rgba(col, alpha);
|
||||
ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke();
|
||||
}
|
||||
},
|
||||
particles: { type: 'particles', count: 30, color: '#88CCFF' }
|
||||
},
|
||||
|
||||
satellites: {
|
||||
meta: { id: 'space_satellites', mood: 'calm', colors: ['#060615', '#101030', '#334466', '#AACCDD'], tags: ['orbital', 'tech', 'night'], description: 'Satellites orbiting above a dark Earth', compatibleMusicMoods: ['ambient', 'calm', 'dreamy'], recommendedFor: ['puzzle', 'trivia', 'creative'] },
|
||||
colors: ['#060615', '#101030', '#334466', '#AACCDD'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90);
|
||||
// Earth curve at bottom
|
||||
var g = Draw.radialGradient(ctx, w * 0.5, h * 1.6, h * 0.9, [[0, '#1A3355'], [0.5, '#0D1B2A'], [1, H.rgba(c[0], 0)]]);
|
||||
ctx.fillStyle = g;
|
||||
ctx.beginPath(); ctx.arc(w * 0.5, h * 1.6, h * 0.9, 0, Math.PI * 2); ctx.fill();
|
||||
// Atmosphere glow
|
||||
Draw.glow(ctx, w * 0.5, h * 1.05, h * 0.3, H.rgba('#3388CC', 0.15));
|
||||
Draw.stars(ctx, w, h * 0.7, 120, '#FFFFFF', 70, [0.3, 1.5]);
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
var rng = H.seededRandom(700);
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var orbit = 0.15 + rng() * 0.5;
|
||||
var speed = 0.1 + rng() * 0.25;
|
||||
var angle = t * speed + rng() * Math.PI * 2;
|
||||
var ox = w * 0.5 + Math.cos(angle) * w * orbit;
|
||||
var oy = h * 0.5 + Math.sin(angle) * h * orbit * 0.3;
|
||||
// Satellite body
|
||||
Draw.rect(ctx, ox - 4, oy - 2, 8, 4, c[2]);
|
||||
// Solar panels
|
||||
Draw.rect(ctx, ox - 14, oy - 1, 9, 2, H.rgba(c[3], 0.7));
|
||||
Draw.rect(ctx, ox + 5, oy - 1, 9, 2, H.rgba(c[3], 0.7));
|
||||
// Signal blink
|
||||
var blink = Math.sin(t * 4 + i * 2) > 0.5 ? 0.8 : 0.1;
|
||||
Draw.circle(ctx, ox, oy, 1.5, H.rgba('#FF4444', blink));
|
||||
}
|
||||
// Orbit lines (faint)
|
||||
ctx.strokeStyle = H.rgba(c[2], 0.08);
|
||||
ctx.lineWidth = 0.5;
|
||||
for (var j = 0; j < 3; j++) {
|
||||
var orb = 0.2 + j * 0.15;
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(w * 0.5, h * 0.5, w * orb, h * orb * 0.3, 0, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
particles: { type: 'stars', count: 40, color: '#FFFFFF' }
|
||||
},
|
||||
|
||||
blackHole: {
|
||||
meta: { id: 'space_blackHole', mood: 'intense', colors: ['#000000', '#0A0510', '#220A33', '#FF6622'], tags: ['dark', 'gravity', 'dramatic'], description: 'A black hole bending light around its event horizon', compatibleMusicMoods: ['tense', 'epic', 'ambient'], recommendedFor: ['action', 'rpg', 'physics'] },
|
||||
colors: ['#000000', '#0A0510', '#220A33', '#FF6622'],
|
||||
renderBase: function(ctx, w, h, c, Draw, t) {
|
||||
Draw.fillGradient(ctx, w, h, [c[0], c[0], c[1]], 90);
|
||||
Draw.stars(ctx, w, h, 80, '#FFFFFF', 80, [0.3, 1]);
|
||||
},
|
||||
renderMid: function(ctx, w, h, c, Draw, t) {
|
||||
var cx = w * 0.5, cy = h * 0.45;
|
||||
var r = Math.min(w, h) * 0.15;
|
||||
// Accretion disk
|
||||
var rng = H.seededRandom(800);
|
||||
ctx.save();
|
||||
ctx.translate(cx, cy);
|
||||
for (var ring = 5; ring >= 0; ring--) {
|
||||
var rr = r * (1.3 + ring * 0.4);
|
||||
var alpha = 0.08 + ring * 0.04;
|
||||
var spin = t * (0.2 + ring * 0.05);
|
||||
ctx.save();
|
||||
ctx.rotate(spin);
|
||||
var col = ring < 3 ? c[3] : c[2];
|
||||
ctx.strokeStyle = H.rgba(col, alpha + Math.sin(t + ring) * 0.03);
|
||||
ctx.lineWidth = 3 + ring * 1.5;
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(0, 0, rr, rr * 0.22, 0, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
// Core glow
|
||||
Draw.glow(ctx, 0, 0, r * 1.5, H.rgba(c[3], 0.35 + Math.sin(t * 1.5) * 0.1));
|
||||
Draw.glow(ctx, 0, 0, r * 0.8, H.rgba(c[2], 0.4));
|
||||
// Black center
|
||||
var gc = Draw.radialGradient(ctx, 0, 0, r * 0.6, [[0, '#000000'], [0.7, '#000000'], [1, H.rgba('#000000', 0)]]);
|
||||
ctx.fillStyle = gc;
|
||||
ctx.beginPath(); ctx.arc(0, 0, r * 0.6, 0, Math.PI * 2); ctx.fill();
|
||||
ctx.restore();
|
||||
// Gravitational lensing — bent stars near hole
|
||||
for (var i = 0; i < 12; i++) {
|
||||
var a = rng() * Math.PI * 2;
|
||||
var d = r * (1.1 + rng() * 0.8);
|
||||
var sx = cx + Math.cos(a + t * 0.15) * d;
|
||||
var sy = cy + Math.sin(a + t * 0.15) * d * 0.4;
|
||||
var stretch = 1 + 2 / (1 + d / r);
|
||||
ctx.fillStyle = H.rgba('#FFCC88', 0.4 + rng() * 0.3);
|
||||
ctx.beginPath(); ctx.ellipse(sx, sy, 1.5 * stretch, 1, a, 0, Math.PI * 2); ctx.fill();
|
||||
}
|
||||
},
|
||||
particles: { type: 'particles', count: 35, color: '#FF8844' }
|
||||
}
|
||||
});
|
||||
|
||||
})(window.BackgroundEngine);
|
||||
Reference in New Issue
Block a user