1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8" />
5<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6<title>Soft Drifting Starfield</title>
7<style>
8 :root{
9 --bg1:#070a14;
10 --bg2:#0d1420;
11 --bg3:#162433;
12 }
13
14 html,body{
15 margin:0;
16 width:100%;
17 height:100%;
18 overflow:hidden;
19 background:#05070d;
20 }
21
22 body{
23 position:relative;
24 font-family:sans-serif;
25 }
26
27 .scene{
28 position:fixed;
29 inset:0;
30 background:
31 radial-gradient(ellipse at center 72%, rgba(34,52,70,.55) 0%, rgba(18,28,42,.28) 34%, rgba(10,15,25,.08) 58%, rgba(0,0,0,0) 78%),
32 radial-gradient(ellipse at center, #111a27 0%, #0b1220 48%, #070b14 78%, #04060c 100%);
33 }
34
35 canvas{
36 position:absolute;
37 inset:0;
38 width:100%;
39 height:100%;
40 display:block;
41 filter: blur(.15px);
42 }
43
44 .vignette{
45 position:absolute;
46 inset:0;
47 pointer-events:none;
48 background:
49 radial-gradient(circle at center, rgba(0,0,0,0) 48%, rgba(0,0,0,.18) 78%, rgba(0,0,0,.42) 100%);
50 mix-blend-mode:normal;
51 }
52</style>
53</head>
54<body>
55 <div class="scene">
56 <canvas id="c"></canvas>
57 <div class="vignette"></div>
58 </div>
59
60<script>
61(() => {
62 const canvas = document.getElementById('c');
63 const ctx = canvas.getContext('2d', { alpha: true });
64
65 let w = 0, h = 0, dpr = Math.min(devicePixelRatio || 1, 2);
66 let stars = [];
67 const STAR_COUNT = 145;
68
69 function rand(a, b){ return a + Math.random() * (b - a); }
70 function lerp(a,b,t){ return a + (b-a)*t; }
71
72 function resize(){
73 w = innerWidth;
74 h = innerHeight;
75 dpr = Math.min(devicePixelRatio || 1, 2);
76 canvas.width = Math.round(w * dpr);
77 canvas.height = Math.round(h * dpr);
78 ctx.setTransform(dpr,0,0,dpr,0,0);
79 buildStars();
80 }
81
82 function buildStars(){
83 stars = [];
84 for(let i=0;i<STAR_COUNT;i++){
85 const bright = Math.random();
86 const r =
87 bright > 0.92 ? rand(3.2, 4.8) :
88 bright > 0.72 ? rand(2.1, 3.2) :
89 bright > 0.35 ? rand(1.3, 2.2) :
90 rand(0.55, 1.25);
91
92 stars.push({
93 x: rand(-20, w + 20),
94 y: rand(-20, h + 20),
95 r,
96 a: bright > 0.8 ? rand(0.72, 0.98) : rand(0.22, 0.72),
97 glow: bright > 0.78 ? rand(8, 18) : rand(0, 7),
98 driftX: rand(-0.9, 0.9),
99 driftY: rand(-1.2, 1.2),
100 phase: rand(0, Math.PI * 2),
101 twinkleAmp: bright > 0.8 ? rand(0.08, 0.22) : rand(0.03, 0.12),
102 twinkleSpeed: rand(0.18, 0.55),
103 pulseOffset: rand(0, 1000)
104 });
105 }
106 }
107
108 function drawStar(s, t){
109 const nx = s.x + Math.sin(t * 0.00018 + s.phase) * 10 + s.driftX * t * 0.0025;
110 const ny = s.y + Math.cos(t * 0.00014 + s.phase * 1.7) * 8 + s.driftY * t * 0.0022;
111
112 let x = nx, y = ny;
113
114 x = ((x % (w + 40)) + (w + 40)) % (w + 40) - 20;
115 y = ((y % (h + 40)) + (h + 40)) % (h + 40) - 20;
116
117 const tw = 1 + Math.sin(t * 0.001 * s.twinkleSpeed + s.phase * 3.1) * s.twinkleAmp;
118 const alpha = Math.max(0.06, Math.min(1, s.a * tw));
119 const rr = s.r * (0.96 + 0.08 * Math.sin((t + s.pulseOffset) * 0.0012));
120
121 if (s.glow > 0.5){
122 const g = ctx.createRadialGradient(x, y, 0, x, y, s.glow * tw);
123 g.addColorStop(0, `rgba(255,255,255,${alpha * 0.22})`);
124 g.addColorStop(0.35, `rgba(235,242,255,${alpha * 0.12})`);
125 g.addColorStop(1, `rgba(220,230,255,0)`);
126 ctx.fillStyle = g;
127 ctx.beginPath();
128 ctx.arc(x, y, s.glow * tw, 0, Math.PI * 2);
129 ctx.fill();
130 }
131
132 ctx.fillStyle = `rgba(245,248,255,${alpha})`;
133 ctx.beginPath();
134 ctx.arc(x, y, rr, 0, Math.PI * 2);
135 ctx.fill();
136 }
137
138 function render(t){
139 ctx.clearRect(0,0,w,h);
140
141 const bg = ctx.createRadialGradient(w * 0.5, h * 0.78, 0, w * 0.5, h * 0.78, Math.max(w,h) * 0.9);
142 bg.addColorStop(0, 'rgba(28,43,58,0.10)');
143 bg.addColorStop(0.45, 'rgba(18,28,42,0.05)');
144 bg.addColorStop(1, 'rgba(0,0,0,0)');
145 ctx.fillStyle = bg;
146 ctx.fillRect(0,0,w,h);
147
148 for(const s of stars) drawStar(s, t);
149
150 requestAnimationFrame(render);
151 }
152
153 addEventListener('resize', resize, { passive:true });
154 resize();
155 requestAnimationFrame(render);
156})();
157</script>
158</body>
159</html>