Back to Components
Three.js Solar Core Animation – Full Code Explanation
Component

Three.js Solar Core Animation – Full Code Explanation

CodewithLord
December 2, 2025

Detailed breakdown of the HTML, CSS, and JavaScript used to create an interactive 3D solar-core animation using Three.js, shaders, and particle systems.

🔥 Three.js Solar Core Animation – Complete Explanation

This document explains the entire structure of the HTML, CSS, and JavaScript used in your interactive 3D solar-core project.
Each section is explained separately — HTML, CSS, and JavaScript.


🟧 HTML Explanation

The HTML defines the UI and loads external modules.

1. Page Setup

  • The document uses a minimal HTML structure.
  • It sets the title to Solaris Core and prepares the page for rendering a full-screen 3D canvas.

2. Overlay Information

  • A small text overlay is displayed at the top-left.
  • It shows the project name and basic instructions like “Drag to orbit” and “Scroll to zoom”.
  • This overlay does not interfere with mouse events on the canvas.

3. Theme Switch Button

  • A button is placed at the top-right.
  • It cycles through themes like Solaris, Nebula, and Supernova.
  • This button updates the shader colors dynamically.

4. Import Map

  • Import maps are used to load Three.js modules (like OrbitControls).
  • This avoids long relative paths and makes imports cleaner.

🟦 CSS Explanation

The CSS makes the project visually clean and fully immersive.

1. Reset & Fullscreen Layout

  • All margins and paddings are cleared.
  • html and body are set to fullscreen and given a black background.
  • Overflow is hidden to prevent unwanted scrolling.

2. Canvas Styling

  • The 3D canvas is set as a fixed background layer.
  • It always covers 100% width and height of the screen.

3. Overlay Text Styling

  • Positioned at the top-left.
  • Light opacity so it doesn’t distract from the 3D animation.
  • White color for maximum readability against the dark background.

4. Theme Button Styling

  • The button has modern UI styling using:
    • Glassmorphism effects (backdrop-filter: blur)
    • Rounded edges
    • Light backgrounds
  • The active theme button has a highlighted background.

🟩 JavaScript Explanation

The JavaScript builds the entire 3D environment using Three.js and shaders.

1. Setting Up Renderer, Scene, and Camera

  • A WebGL renderer is created with anti-aliasing.
  • A 3D scene object holds all mesh and particle elements.
  • A PerspectiveCamera is used to give a realistic depth look.

2. Orbit Controls

  • Allows the user to rotate around the star.
  • Smooth damping creates natural camera movement.
  • Zooming is enabled with scroll.

3. Theme System

  • A theme object stores color combinations for:
    • Core glow
    • Accretion disk
    • Particle embers
    • Outer shell
  • Pressing the theme button cycles through these color schemes.

4. Core Star Shader

  • A custom ShaderMaterial is used.
  • Noise-based vertex displacement simulates a pulsating star.
  • A Fresnel effect adds a thin glowing outline.
  • Colors change dynamically based on the selected theme.

5. Accretion Disk Particles

  • 30,000 small particles rotate around the core.
  • They simulate a sci-fi glowing energy disk.
  • Particles slightly vibrate and animate over time for realism.

6. Ember Particles

  • Small glowing particles shoot outward.
  • Each ember has:
    • Random start position
    • Random direction
    • A lifespan cycle
  • Creates a dynamic "energy burst" effect around the star.

7. Animation Loop

  • Updates controls, shaders, and particles.
  • Renders using post-processing.

8. Post-Processing Effects

The animation uses:

  • UnrealBloomPass for glow
  • FXAA for antialiasing
  • AfterimagePass for subtle motion trails

These effects enhance the cinematic sci-fi look.


1<!DOCTYPE html> 2<html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>Solaris Core</title> 7 8 9 </head> 10 11 <body> 12 <style> 13 * { margin:0; padding:0; box-sizing: border-box; } 14 html, body { 15 height:100%; 16 background:#000; 17 overflow:hidden; 18 font-family: 'Inter', sans-serif; 19 } 20 canvas { 21 width:100%; 22 height:100%; 23 display:block; 24 position:fixed; 25 top:0; 26 left:0; 27 } 28 .overlay { 29 position: fixed; 30 top: 1rem; 31 left: 1rem; 32 color: white; 33 text-shadow: 0 0 5px black; 34 pointer-events: none; 35 opacity: 0.7; 36 } 37 .overlay h1 { 38 font-size: 1.5rem; 39 font-weight: 500; 40 } 41 .overlay p { 42 font-size: 0.9rem; 43 font-weight: 300; 44 } 45 .theme-switcher { 46 position: fixed; 47 top: 1rem; 48 right: 1rem; 49 display: flex; 50 gap: 0.5rem; 51 z-index: 10; 52 } 53 .theme-button { 54 padding: 0.5rem 1rem; 55 font-size: 0.9rem; 56 font-family: 'Inter', sans-serif; 57 color: white; 58 background: rgba(255, 255, 255, 0.1); 59 border: 1px solid rgba(255, 255, 255, 0.2); 60 border-radius: 12px; 61 backdrop-filter: blur(10px) saturate(180%); 62 -webkit-backdrop-filter: blur(10px) saturate(180%); 63 cursor: pointer; 64 transition: background 0.3s ease, border-color 0.3s ease; 65 } 66 .theme-button:hover { 67 background: rgba(255, 255, 255, 0.2); 68 } 69 .theme-button.active { 70 background: rgba(255, 255, 255, 0.25); 71 border-color: rgba(255, 255, 255, 0.4); 72 font-weight: 500; 73 } 74</style> 75<div class="overlay"> 76 <h1 id="theme-title">Solaris</h1> 77 <p>Drag to orbit. Scroll to zoom.</p> 78</div> 79<div class="theme-switcher"> 80 <button class="theme-button" id="theme-cycle-button">Solaris</button> 81</div> 82<script type="importmap"> 83{ 84 "imports": { 85 "three": "https://cdn.jsdelivr.net/npm/three@0.162.0/build/three.module.js", 86 "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.162.0/examples/jsm/" 87 } 88} 89</script> 90<script type="module"> 91import * as THREE from 'three'; 92import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 93import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; 94import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; 95import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; 96import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; 97import { FXAAShader } from 'three/addons/shaders/FXAAShader.js'; 98import { AfterimagePass } from 'three/addons/postprocessing/AfterimagePass.js'; 99 100const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: 'high-performance' }); 101const DPR = Math.min(window.devicePixelRatio || 1, 2); 102renderer.setPixelRatio(DPR); 103renderer.setSize(window.innerWidth, window.innerHeight); 104renderer.setClearColor(0x000000, 1); 105renderer.outputColorSpace = THREE.SRGBColorSpace; 106renderer.toneMapping = THREE.ACESFilmicToneMapping; 107renderer.toneMappingExposure = 1.08; 108document.body.appendChild(renderer.domElement); 109const scene = new THREE.Scene(); 110const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 3000); 111camera.position.set(0, 8, 35); 112const controls = new OrbitControls(camera, renderer.domElement); 113controls.enableDamping = true; 114controls.dampingFactor = 0.05; 115controls.minDistance = 5; 116controls.maxDistance = 80; 117 118const THEMES = { 119 solaris: { 120 name: "Solaris", 121 colors: { 122 core: new THREE.Color(1.00, 0.90, 0.70), 123 shell: new THREE.Color(1.00, 0.55, 0.10), 124 diskA: new THREE.Color(1.00, 0.65, 0.10), 125 diskB: new THREE.Color(0.98, 0.25, 0.08), 126 emberA: new THREE.Color(1.00, 0.95, 0.80), 127 emberB: new THREE.Color(1.00, 0.55, 0.05), 128 prominence: new THREE.Color(1.00, 0.65, 0.10) 129 } 130 }, 131 nebula: { 132 name: "Nebula", 133 colors: { 134 core: new THREE.Color(0.8, 0.9, 1.0), 135 shell: new THREE.Color(0.1, 0.4, 1.0), 136 diskA: new THREE.Color(0.2, 0.5, 1.0), 137 diskB: new THREE.Color(0.6, 0.2, 0.9), 138 emberA: new THREE.Color(0.9, 0.95, 1.0), 139 emberB: new THREE.Color(0.3, 0.5, 1.0), 140 prominence: new THREE.Color(0.5, 0.3, 1.0) 141 } 142 }, 143 supernova: { 144 name: "Supernova", 145 colors: { 146 core: new THREE.Color(1.0, 0.9, 0.95), 147 shell: new THREE.Color(1.0, 0.1, 0.2), 148 diskA: new THREE.Color(1.0, 0.2, 0.4), 149 diskB: new THREE.Color(0.9, 0.1, 0.8), 150 emberA: new THREE.Color(1.0, 0.95, 0.95), 151 emberB: new THREE.Color(1.0, 0.3, 0.3), 152 prominence: new THREE.Color(1.0, 0.2, 0.5) 153 } 154 } 155}; 156 157const noiseFunctions = ` 158 vec3 mod289(vec3 x){return x - floor(x*(1.0/289.0))*289.0;} 159 vec4 mod289(vec4 x){return x - floor(x*(1.0/289.0))*289.0;} 160 vec4 permute(vec4 x){return mod289(((x*34.0)+1.0)*x);} 161 vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;} 162 float snoise(vec3 v){ 163 const vec2 C=vec2(1.0/6.0,1.0/3.0); 164 const vec4 D=vec4(0.0,0.5,1.0,2.0); 165 vec3 i=floor(v+dot(v,C.yyy)); 166 vec3 x0=v-i+dot(i,C.xxx); 167 vec3 g=step(x0.yzx,x0.xyz); 168 vec3 l=1.0-g; 169 vec3 i1=min(g.xyz,l.zxy); 170 vec3 i2=max(g.xyz,l.zxy); 171 vec3 x1=x0-i1+C.xxx; 172 vec3 x2=x0-i2+C.yyy; 173 vec3 x3=x0-D.yyy; 174 i=mod289(i); 175 vec4 p=permute(permute(permute(i.z+vec4(0.0,i1.z,i2.z,1.0))+i.y+vec4(0.0,i1.y,i2.y,1.0))+i.x+vec4(0.0,i1.x,i2.x,1.0)); 176 float n_=0.142857142857; 177 vec3 ns=n_*D.wyz-D.xzx; 178 vec4 j=p-49.0*floor(p*ns.z*ns.z); 179 vec4 x_=floor(j*ns.z); 180 vec4 y_=floor(j-7.0*x_); 181 vec4 x=x_*ns.x+ns.yyyy; 182 vec4 y=y_*ns.x+ns.yyyy; 183 vec4 h=1.0-abs(x)-abs(y); 184 vec4 b0=vec4(x.xy,y.xy); vec4 b1=vec4(x.zw,y.zw); 185 vec4 s0=floor(b0)*2.0+1.0; vec4 s1=floor(b1)*2.0+1.0; 186 vec4 sh=-step(h,vec4(0.0)); 187 vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy; vec4 a1=b1.xzyw+s1.xzyw*sh.zzww; 188 vec3 p0=vec3(a0.xy,h.x); vec3 p1=vec3(a0.zw,h.y); vec3 p2=vec3(a1.xy,h.z); vec3 p3=vec3(a1.zw,h.w); 189 vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3))); 190 p0*=norm.x; p1*=norm.y; p2*=norm.z; p3*=norm.w; 191 vec4 m=max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0); 192 m=m*m; 193 return 42.0*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3))); 194 } 195`; 196 197const coreGroup = new THREE.Group(); 198scene.add(coreGroup); 199 200const starGeometry = new THREE.IcosahedronGeometry(4, 5); 201const starMaterial = new THREE.ShaderMaterial({ 202 uniforms: { time: { value: 0 }, uCore: { value: THEMES.solaris.colors.core.clone() } }, 203 vertexShader: ` 204 uniform float time; 205 varying vec3 vN; 206 ${noiseFunctions} 207 void main(){ 208 vN = normalize(normal); 209 float displacement = snoise(normal * 4.0 + time * 0.8) * 0.45; 210 vec3 newPosition = position + normal * displacement; 211 gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); 212 } 213 `, 214 fragmentShader: ` 215 uniform float time; 216 uniform vec3 uCore; 217 varying vec3 vN; 218 ${noiseFunctions} 219 void main(){ 220 float pulse = pow(0.5 + 0.5*sin(time*2.15), 1.7); 221 float fres = pow(1.0 - abs(dot(vN, vec3(0,0,1))), 3.0); 222 float surfaceNoise = snoise(vN * 8.0 + time * 1.2); 223 vec3 col = uCore * (0.4 + 1.5*fres) * (0.4 + 1.0*pulse) * (1.0 + 0.2 * surfaceNoise); 224 col = clamp(col, 0.0, 0.95); 225 gl_FragColor = vec4(col, 1.0); 226 } 227 `, 228 blending: THREE.AdditiveBlending, depthWrite: false 229}); 230coreGroup.add(new THREE.Mesh(starGeometry, starMaterial)); 231 232const shellGeometry = new THREE.IcosahedronGeometry(8, 5); 233const shellMaterial = new THREE.ShaderMaterial({ 234 uniforms: { time: { value: 0 }, uShell: { value: THEMES.solaris.colors.shell.clone() } }, 235 vertexShader: ` 236 uniform float time; 237 varying vec3 vN; 238 varying vec2 vUv; 239 ${noiseFunctions} 240 void main(){ 241 vN = normalize(normal); 242 vUv = uv; 243 float displacement = snoise(position * 2.0 + time * 0.5) * 1.2; 244 vec3 newPosition = position + normal * displacement; 245 gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition,1.0); 246 } 247 `, 248 fragmentShader: ` 249 uniform float time; 250 uniform vec3 uShell; 251 varying vec3 vN; 252 varying vec2 vUv; 253 ${noiseFunctions} 254 void main(){ 255 float fres = pow(1.0 - abs(dot(vN, vec3(0,0,1))), 0.6); 256 float n = snoise(vec3(vUv*8.0 + vec2(time*0.3, 0.0), time*0.3)); 257 float fil = smoothstep(0.55, 0.82, n) * pow(abs(vUv.y*2.0 - 1.0), 14.0); 258 vec3 color = uShell * (0.1 + 2.0*fil + 0.8*fres); 259 float alpha = clamp(0.1 + 0.6*fres + 0.7*fil, 0.0, 1.0); 260 gl_FragColor = vec4(color, alpha); 261 } 262 `, 263 transparent: true, blending: THREE.AdditiveBlending, depthWrite: false 264}); 265coreGroup.add(new THREE.Mesh(shellGeometry, shellMaterial)); 266 267const particleCount = 30000; 268const diskPositions = new Float32Array(particleCount * 3); 269const diskSeeds = new Float32Array(particleCount); 270const diskBands = new Float32Array(particleCount); 271for (let i = 0; i < particleCount; i++) { 272 const r = 8 + Math.random()*20; 273 const theta = Math.random()*Math.PI*2; 274 diskPositions[i*3] = Math.cos(theta)*r; 275 diskPositions[i*3 + 1] = (Math.random() - 0.5) * 4.0; 276 diskPositions[i*3 + 2] = Math.sin(theta)*r; 277 diskSeeds[i] = Math.random()*1000.0; 278 diskBands[i] = (r - 8.0) / 20.0; 279} 280const diskGeom = new THREE.BufferGeometry(); 281diskGeom.setAttribute('position', new THREE.BufferAttribute(diskPositions, 3)); 282diskGeom.setAttribute('aSeed', new THREE.BufferAttribute(diskSeeds, 1)); 283diskGeom.setAttribute('aBand', new THREE.BufferAttribute(diskBands, 1)); 284const diskMat = new THREE.ShaderMaterial({ 285 uniforms: { uColorA: { value: THEMES.solaris.colors.diskA.clone() }, uColorB: { value: THEMES.solaris.colors.diskB.clone() }, time: { value: 0 } }, 286 vertexShader: ` 287 uniform float time; 288 attribute float aSeed; 289 attribute float aBand; 290 varying float vMix; 291 varying float vAlpha; 292 vec2 rot(vec2 p, float a){ float c=cos(a), s=sin(a); return vec2(c*p.x - s*p.y, s*p.x + c*p.y); } 293 void main(){ 294 vec3 p = position; 295 float r = length(p.xz); 296 float speed = (14.5 / max(16.0, r*r)); 297 float angle = -time * speed; 298 vec2 xz = rot(p.xz, angle); 299 float breathe = 1.0 + 0.011*sin(time*0.8 + aSeed); 300 p.xz = xz * breathe; 301 p.y *= (1.0 + 0.2*sin(time*1.4 + aSeed*2.0 + r*0.2)); 302 vec4 mvp = modelViewMatrix * vec4(p, 1.0); 303 gl_Position = projectionMatrix * mvp; 304 gl_PointSize = (65.0 / -mvp.z) * (1.0 - aBand); 305 vMix = aBand; 306 vAlpha = 0.4 + 0.4 * sin(time*3.0 + aSeed); 307 } 308 `, 309 fragmentShader: ` 310 uniform vec3 uColorA; 311 uniform vec3 uColorB; 312 varying float vMix; 313 varying float vAlpha; 314 void main(){ 315 if (length(gl_PointCoord - vec2(0.5)) > 0.5) discard; 316 vec3 col = mix(uColorA, uColorB, vMix); 317 gl_FragColor = vec4(col * 1.2, vAlpha); 318 } 319 `, 320 transparent: true, blending: THREE.AdditiveBlending, depthWrite: false 321}); 322scene.add(new THREE.Points(diskGeom, diskMat)); 323 324const emberCount = 5000; 325const emberPos = new Float32Array(emberCount * 3); 326const emberSeeds = new Float32Array(emberCount * 4); 327for (let i = 0; i < emberCount; i++) { 328 emberPos.set([0,0,0], i*3); 329 emberSeeds.set([Math.random(), 0.1 + Math.random()*0.9, Math.random()*10, 0.5 + Math.random()], i*4); 330} 331const emberGeom = new THREE.BufferGeometry(); 332emberGeom.setAttribute('position', new THREE.BufferAttribute(emberPos, 3)); 333emberGeom.setAttribute('aSeed', new THREE.BufferAttribute(emberSeeds, 4)); 334const emberMat = new THREE.ShaderMaterial({ 335 uniforms: { uEmberA: { value: THEMES.solaris.colors.emberA.clone() }, uEmberB: { value: THEMES.solaris.colors.emberB.clone() }, time: { value: 0 }}, 336 vertexShader: ` 337 uniform float time; 338 attribute vec4 aSeed; 339 varying float vLife; 340 void main() { 341 float life = mod(time * aSeed.y * 0.3 + aSeed.x, 1.0); 342 vec3 p = normalize(vec3( 343 sin(aSeed.z * 1.2), 344 cos(aSeed.z * 1.7), 345 sin(aSeed.z * 1.1) 346 )) * (8.0 + life * 60.0); 347 vec4 mvPosition = modelViewMatrix * vec4(p, 1.0); 348 gl_PointSize = (150.0 / -mvPosition.z) * (1.0 - life) * aSeed.w; 349 gl_Position = projectionMatrix * mvPosition; 350 vLife = life; 351 } 352 `, 353 fragmentShader: ` 354 uniform vec3 uEmberA; 355 uniform vec3 uEmberB; 356 varying float vLife; 357 void main() { 358 if (length(gl_PointCoord - vec2(0.5)) > 0.5) discard; 359 float opacity = pow(1.0 - vLife, 2.0); 360 vec3 col = mix(uEmberA, uEmberB, vLife); 361 gl_FragColor = vec4(col * 1.4, opacity); 362 } 363 `, 364 transparent: true, blending: THREE.AdditiveBlending, depthWrite: false 365}); 366scene.add(new THREE.Points(emberGeom, emberMat)); 367 368const prominenceCount = 1000; 369const prominencePos = new Float32Array(prominenceCount * 3); 370const prominenceSeeds = new Float32Array(prominenceCount * 4); 371const q = new THREE.Quaternion(); 372const v = new THREE.Vector3(); 373for (let i = 0; i < prominenceCount; i++) { 374 prominencePos.set([0, 0, 0], i * 3); 375 prominenceSeeds.set([ 376 Math.random(), 377 0.1 + Math.random() * 0.4, 378 5.0 + Math.random() * 25.0, 379 0.5 + Math.random() * 1.5 380 ], i * 4); 381} 382const prominenceGeom = new THREE.BufferGeometry(); 383prominenceGeom.setAttribute('position', new THREE.BufferAttribute(prominencePos, 3)); 384prominenceGeom.setAttribute('aSeed', new THREE.BufferAttribute(prominenceSeeds, 4)); 385const prominenceMat = new THREE.ShaderMaterial({ 386 uniforms: { 387 uColor: { value: THEMES.solaris.colors.prominence.clone() }, 388 time: { value: 0 } 389 }, 390 vertexShader: ` 391 uniform float time; 392 attribute vec4 aSeed; 393 varying float vLife; 394 395 vec4 quat_from_axis_angle(vec3 axis, float angle) { 396 vec4 qr; 397 float half_angle = (angle * 0.5); 398 qr.x = axis.x * sin(half_angle); 399 qr.y = axis.y * sin(half_angle); 400 qr.z = axis.z * sin(half_angle); 401 qr.w = cos(half_angle); 402 return qr; 403 } 404 405 vec3 rotate_vertex_position(vec3 position, vec4 q) { 406 return position + 2.0 * cross(q.xyz, cross(q.xyz, position) + q.w * position); 407 } 408 409 void main() { 410 float life = mod(time * aSeed.y + aSeed.x, 1.0); 411 vLife = life; 412 413 float arc = sin(life * 3.14159); 414 vec3 p = vec3(0.0, 0.0, 0.0); 415 p.y = arc * aSeed.z; 416 p.x = (life - 0.5) * 16.0; 417 418 vec3 axis = normalize(vec3(aSeed.x - 0.5, aSeed.y - 0.5, aSeed.z - 0.5)); 419 float angle = aSeed.x * 6.28318; 420 vec4 q = quat_from_axis_angle(axis, angle); 421 p = rotate_vertex_position(p, q); 422 423 p += normalize(p) * 8.0; 424 425 vec4 mvPosition = modelViewMatrix * vec4(p, 1.0); 426 gl_PointSize = (250.0 / -mvPosition.z) * arc * (1.0 - life) * aSeed.w; 427 gl_Position = projectionMatrix * mvPosition; 428 } 429 `, 430 fragmentShader: ` 431 uniform vec3 uColor; 432 varying float vLife; 433 void main() { 434 if (length(gl_PointCoord - vec2(0.5)) > 0.5) discard; 435 float opacity = pow(sin(vLife * 3.14159), 1.5) * 0.8; 436 gl_FragColor = vec4(uColor * 1.5, opacity); 437 } 438 `, 439 transparent: true, 440 blending: THREE.AdditiveBlending, 441 depthWrite: false 442}); 443coreGroup.add(new THREE.Points(prominenceGeom, prominenceMat)); 444 445{ 446 const count = 2400; 447 const pos = new Float32Array(count * 3); 448 for (let i=0;i<count;i++){ 449 const r = THREE.MathUtils.randFloat(250, 1000); 450 const th = Math.random()*Math.PI*2; 451 const ph = Math.acos(THREE.MathUtils.randFloatSpread(2)); 452 pos[i*3] = r * Math.sin(ph) * Math.cos(th); 453 pos[i*3+1] = r * Math.cos(ph); 454 pos[i*3+2] = r * Math.sin(ph) * Math.sin(th); 455 } 456 const g = new THREE.BufferGeometry(); 457 g.setAttribute('position', new THREE.BufferAttribute(pos,3)); 458 const m = new THREE.PointsMaterial({ color: 0xffffff, size: 0.8, sizeAttenuation: true, transparent:true, opacity:0.5, depthWrite:false }); 459 scene.add(new THREE.Points(g,m)); 460} 461 462const composer = new EffectComposer(renderer); 463composer.addPass(new RenderPass(scene, camera)); 464composer.addPass(new AfterimagePass(0.92)); 465const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.2, 0.7, 0.92); 466composer.addPass(bloomPass); 467const fxaaPass = new ShaderPass(FXAAShader); 468fxaaPass.material.uniforms['resolution'].value.x = 1 / (window.innerWidth * DPR); 469fxaaPass.material.uniforms['resolution'].value.y = 1 / (window.innerHeight * DPR); 470composer.addPass(fxaaPass); 471 472const themeTitleElement = document.getElementById('theme-title'); 473const cycleButton = document.getElementById('theme-cycle-button'); 474const themeOrder = ['solaris', 'nebula', 'supernova']; 475let currentThemeIndex = 0; 476 477function applyTheme(themeName) { 478 const theme = THEMES[themeName]; 479 if (!theme) return; 480 481 const colors = theme.colors; 482 starMaterial.uniforms.uCore.value.set(colors.core); 483 shellMaterial.uniforms.uShell.value.set(colors.shell); 484 diskMat.uniforms.uColorA.value.set(colors.diskA); 485 diskMat.uniforms.uColorB.value.set(colors.diskB); 486 emberMat.uniforms.uEmberA.value.set(colors.emberA); 487 emberMat.uniforms.uEmberB.value.set(colors.emberB); 488 prominenceMat.uniforms.uColor.value.set(colors.prominence); 489 490 themeTitleElement.textContent = theme.name; 491 cycleButton.textContent = theme.name; 492} 493 494cycleButton.addEventListener('click', () => { 495 currentThemeIndex = (currentThemeIndex + 1) % themeOrder.length; 496 const nextThemeName = themeOrder[currentThemeIndex]; 497 applyTheme(nextThemeName); 498}); 499 500window.addEventListener('resize', () => { 501 camera.aspect = window.innerWidth / window.innerHeight; 502 camera.updateProjectionMatrix(); 503 renderer.setSize(window.innerWidth, window.innerHeight); 504 composer.setSize(window.innerWidth, window.innerHeight); 505 fxaaPass.material.uniforms['resolution'].value.x = 1 / (window.innerWidth * renderer.getPixelRatio()); 506 fxaaPass.material.uniforms['resolution'].value.y = 1 / (window.innerHeight * renderer.getPixelRatio()); 507}); 508 509const clock = new THREE.Clock(); 510function animate() { 511 requestAnimationFrame(animate); 512 const delta = clock.getDelta(); 513 const time = clock.getElapsedTime(); 514 starMaterial.uniforms.time.value = time; 515 shellMaterial.uniforms.time.value = time; 516 diskMat.uniforms.time.value = time; 517 emberMat.uniforms.time.value = time; 518 prominenceMat.uniforms.time.value = time; 519 520 const pulse = 0.5 + 0.5 * Math.sin(time * 2.15); 521 bloomPass.strength = 0.9 + 0.4 * pulse; 522 523 coreGroup.rotation.y += delta * 0.05; 524 controls.update(); 525 composer.render(); 526} 527 528animate(); 529</script> 530 531 </body> 532 533</html> 534

Love this component?

Explore more components and build amazing UIs.

View All Components