From 27276bbe327f44ed6d93550d29da9409f38a066d Mon Sep 17 00:00:00 2001 From: Ed Chen <37851723+Edwson@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:06:43 +0800 Subject: [PATCH 1/5] =?UTF-8?q?release:=203.4.0=20"Optics"=20=E2=80=94=20t?= =?UTF-8?q?he=20three.js=20tier,=206=20scenes,=20generated=20artifacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + CHANGELOG.md | 39 + README.md | 72 +- galaxy.d.ts | 22 + galaxy.js | 1903 +++++++++++++++++++++++++++++++++++++++++- galaxy.manifest.json | 238 +++++- galaxy.min.js | 2 +- index.html | 6 +- llms.txt | 12 +- package.json | 4 +- test.mjs | 30 + 11 files changed, 2300 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index c90647b..afa481d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ Thumbs.db # Logs *.log + +# Archives (never commit) +*.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b74848..a579176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,45 @@ All notable changes are documented here. Format: [Keep a Changelog](https://keepachangelog.com/); versioning: [SemVer](https://semver.org/). +## [3.4.0] "Optics" — 2026-08-07 +### Added +- **An optional three.js tier — the library still has zero *required* dependencies.** + `registerAnimation` accepts `renderer: "three"`, and a new + `registerThree(name, { defaults, scene })` helper hands a scene the same lifecycle every other + animation gets. three.js is loaded **on demand, by dynamic `import()`, the first time such a + scene mounts** — once per page, shared by all of them. A page that never mounts one requests + nothing. If the load fails the surface paints a still poster, exactly as the WebGL2 tier + degrades when WebGL2 is missing. Bring your own copy with `Galaxy.useThree(THREE)`, or point one + scene elsewhere with `{ threeUrl }`. +- **Six scenes (80 → 86)**, each doing something the 2D and fragment-shader tiers cannot: + - **`eventHorizon`** — photons integrated through the Schwarzschild metric + (`d²u/dφ² = -u + 3/2·rs·u²`), so the photon ring, the Einstein ring of the background + starfield and the lensed far side of the disk emerge from the geodesics rather than being + drawn on. Relativistic Doppler shift and beaming (I ∝ δ⁴) give the disk its real asymmetry. + - **`molecularCloud`** — a 128³ `Data3DTexture` raymarched in the volume's local space, lit from + within by an embedded protostar via a secondary shadow ray (Beer–Lambert) and a + Henyey–Greenstein phase function. Rotation gives true parallax and self-occlusion. + - **`spiralForge`** — 340,000 stars, one draw call, orbits integrated entirely in the vertex + shader against a flat rotation curve; the density-wave arms shear because ω varies with + radius. Blackbody colour from a mass–temperature relation. + - **`ringedWorld`** — Rayleigh single scattering for a blue limb and a reddened terminator, plus + **mutual shadowing**: ring shadow on the planet (carrying its own gaps) and planet shadow on + the rings, both solved analytically from one shared density function. + - **`gravitySim`** — GPGPU. State lives in float render targets and never returns to the CPU; a + kick–drift symplectic leapfrog keeps orbits stable, and two attractors on an eccentric Kepler + orbit raise tidal bridges and tails at each pericentre. The pointer is a third mass. + - **`starGlare`** — a hand-rolled HDR chain with no addons: bright pass, progressive + downsample/upsample bloom, anamorphic streak, lateral chromatic aberration, filmic tonemap. +- `Galaxy.rendererOf(name)` reports `"2d" | "webgl2" | "three"`; the manifest now records each + animation's tier plus an `optionalDependency` block naming the six scenes that use three.js. + +### Fixed +- **A shader whose program failed to build crashed instead of falling back.** `glPosterFallback` + reached for the 2D context unconditionally, but a canvas that already has a live WebGL2 context + can never hand one back — so the "graceful" path threw `Cannot set properties of null`. The + poster now paints the host element with the equivalent CSS gradient when there is no 2D context. + This affected the WebGL2 tier as shipped in 3.3.0, not just the new one. + ## [3.3.0] "Deep Field" — 2026-07-28 ### Added - **A WebGL2 tier — still zero dependencies.** `registerAnimation` accepts diff --git a/README.md b/README.md index 14274b3..1e2d832 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,78 @@ **The universe, one line of code.** -A zero-dependency cosmic **animation** + **UI component** library. Beautiful -canvas backdrops, a themeable UI kit, and a single unified API — no build step, -no framework, ~20 kB gzipped. +A cosmic **animation** + **UI component** library with **zero required +dependencies**. Beautiful canvas backdrops, a themeable UI kit, and a single +unified API — no build step, no framework, ~20 kB gzipped. + +Six of the 86 animations are three.js scenes. three.js is **optional and lazily +loaded**: nothing is requested unless you mount one of those six, and if it +cannot load they paint a still poster instead of an empty box. [![CI](https://github.com/Edwson/GalaxyJS/actions/workflows/ci.yml/badge.svg)](https://github.com/Edwson/GalaxyJS/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -![Zero dependencies](https://img.shields.io/badge/dependencies-0-7c5cff) -![Version](https://img.shields.io/badge/version-3.3-22d3ee) +![Required dependencies](https://img.shields.io/badge/required%20dependencies-0-7c5cff) +![Optional](https://img.shields.io/badge/optional-three.js-22d3ee) +![Version](https://img.shields.io/badge/version-3.4-22d3ee) > Live demo: https://edwson.github.io/GalaxyJS/ (mirror: https://edwson.com/GalaxyJS/) · Open [`index.html`](index.html) locally for the interactive playground. --- -## ✨ What's new in v3.3 "Deep Field" +## ✨ What's new in v3.4 "Optics" + +**Six new animations (80 → 86) and an optional three.js tier.** + +These six needed something the canvas and fragment-shader tiers genuinely cannot give: a scene +graph, a real 3D volume, GPU-resident simulation state, or a multi-pass post-processing chain. +So they declare `renderer: "three"`, and the library loads three.js **on demand, by dynamic +import, the first time one of them mounts** — once per page, shared across all six. A page that +never uses one downloads nothing extra. + +- **`eventHorizon`** — a Schwarzschild black hole *solved*, not painted. Every pixel's photon is + integrated through curved spacetime (`d²u/dφ² = -u + 3/2·rs·u²`), so the photon ring, the + Einstein ring of the background stars, and the far side of the disk bent up over the top all + fall out of the geodesics. The disk carries relativistic Doppler shift and beaming (I ∝ δ⁴) — + the bright/dim asymmetry is physics, not a gradient. +- **`molecularCloud`** — a genuine 128³ `Data3DTexture` raymarched in the volume's own local + space, lit from within by an embedded protostar with Beer–Lambert extinction along a secondary + shadow ray and a Henyey–Greenstein phase function. Rotating it gives real parallax and + self-occlusion, which is exactly what a 2D noise field cannot do. +- **`spiralForge`** — 340,000 stars in one draw call. Each orbit is integrated *in the vertex + shader* against a flat rotation curve, so there is no per-frame CPU work at all; because ω + varies with radius the density-wave arms shear over time. Colour comes from a blackbody locus + driven by a mass–temperature relation, so rare hot O/B stars burn blue in the arms. +- **`ringedWorld`** — a gas giant with Rayleigh single scattering (blue limb, warm reddened + terminator) and **mutual shadowing**: the rings cast a real shadow onto the planet, carrying + their own Cassini-division gaps as bright lines across the disk, and the planet casts a real + shadow back onto the rings. +- **`gravitySim`** — true GPGPU. Particle state lives in float render targets and never returns + to the CPU; a kick–drift (symplectic leapfrog) integrator keeps the disks stable instead of + unwinding. Two attractors on an eccentric Kepler orbit raise real tidal bridges and tails at + every pericentre. The pointer becomes a third mass. +- **`starGlare`** — a hand-rolled HDR chain (no addons): bright pass → progressive + downsample/upsample bloom → anamorphic streak → lateral chromatic aberration → filmic tonemap. + A star's glare blooms and flares as an occluder slides across it. + +```html +
+ +``` + +Bring your own copy to skip the network entirely: + +```js +import * as THREE from "three"; +Galaxy.useThree(THREE); // now nothing is fetched +Galaxy.create("spiralForge", "#hero", { stars: 400000 }); +``` + +`Galaxy.rendererOf(name)` reports `"2d"`, `"webgl2"` or `"three"` if you want to +check before mounting. + +--- + +## v3.3 "Deep Field" **Twenty new animations (60 → 80) and a WebGL2 tier — with no new dependencies.** @@ -50,7 +108,7 @@ to leak. **If WebGL2 is unavailable the surface renders a 2D poster instead of a ## ✨ What's new in v3 "Nova" - **One unified API** — `Galaxy.create(type, target, options)` for every animation. -- **80 canvas & WebGL2 animations** — starfield, warp, black hole, nebula, spiral galaxy, meteors, constellation, particle field, aurora, wormhole, orbits, pulsar, mesh gradient, fireflies, matrix rain, plasma, fireworks, snow, waves, DNA helix, lightning, ripples, comets, confetti, bubbles, fog, synthwave grid, rain, vortex, sparkle, neon tunnel, swarm, ribbons, flow field, dotted globe, heartbeat, equalizer, clock, light rays, radar, embers, typewriter, spirograph, **and 17 new in v3.1** — supernova, quasar, star cluster, cosmic web, eclipse, solar corona, galaxy merge, crystal lattice, moiré, starburst, nebula pillars, ion storm, stardust, orrery, oscilloscope, bokeh, magnetosphere, **and 20 new in v3.3** — lensing, accretion disk, n-body, tidal stream, inspiral, volumetric nebula, star surface, atmosphere, dust lanes, protoplanetary disk, spectrograph, transit curve, radio waterfall, H-R diagram, pulsar timing, gravity well, nebula paint, solar wind, star forge, relativistic jets. +- **86 animations** — starfield, warp, black hole, nebula, spiral galaxy, meteors, constellation, particle field, aurora, wormhole, orbits, pulsar, mesh gradient, fireflies, matrix rain, plasma, fireworks, snow, waves, DNA helix, lightning, ripples, comets, confetti, bubbles, fog, synthwave grid, rain, vortex, sparkle, neon tunnel, swarm, ribbons, flow field, dotted globe, heartbeat, equalizer, clock, light rays, radar, embers, typewriter, spirograph, **and 17 new in v3.1** — supernova, quasar, star cluster, cosmic web, eclipse, solar corona, galaxy merge, crystal lattice, moiré, starburst, nebula pillars, ion storm, stardust, orrery, oscilloscope, bokeh, magnetosphere, **and 20 new in v3.3** — lensing, accretion disk, n-body, tidal stream, inspiral, volumetric nebula, star surface, atmosphere, dust lanes, protoplanetary disk, spectrograph, transit curve, radio waterfall, H-R diagram, pulsar timing, gravity well, nebula paint, solar wind, star forge, relativistic jets, **and 6 new in v3.4 on the optional three.js tier** — event horizon, molecular cloud, spiral forge, ringed world, gravity sim, star glare. - **A real UI kit** — buttons, cards, modals, toasts, tooltips, tabs, accordions, dropdowns, inputs, switches, progress, spinners — all driven by design tokens. - **Theming** — light/dark out of the box, fully tokenized via CSS variables and `Galaxy.theme()`. - **Accessible & efficient** — respects `prefers-reduced-motion`, one shared rAF loop for the whole page, auto-pause off-screen, HiDPI-aware. diff --git a/galaxy.d.ts b/galaxy.d.ts index 2400f2f..969d2d7 100644 --- a/galaxy.d.ts +++ b/galaxy.d.ts @@ -85,6 +85,13 @@ export type AnimationType = | "solarWind" | "starForge" | "relativisticJets" + /* three.js tier — optional dependency, loaded on demand */ + | "eventHorizon" + | "molecularCloud" + | "spiralForge" + | "ringedWorld" + | "gravitySim" + | "starGlare" | "magnetosphere"; /** A CSS selector string or an existing element. */ @@ -104,6 +111,11 @@ export interface AnimationOptions { interactive?: boolean; /** Start automatically on create. Default: true. */ autoplay?: boolean; + /** + * Where to load three.js from, for `"three"`-tier scenes only. Defaults to a + * pinned jsDelivr build. Ignored once `Galaxy.useThree()` has been called. + */ + threeUrl?: string; /** Any animation-specific numeric parameter (count, stars, rings, etc.). */ [key: string]: unknown; } @@ -151,6 +163,9 @@ export interface AnimationDefinition { defaults?: AnimationOptions; } +/** Which renderer draws an animation. */ +export type AnimationRenderer = "2d" | "webgl2" | "three"; + export type ToastType = "success" | "warning" | "danger" | "info"; export type ToastPosition = | "top-right" | "top-left" | "bottom-right" | "bottom-left" | "top-center"; @@ -239,6 +254,13 @@ export interface GalaxyStatic { list(): string[]; /** Get a copy of an animation's default options. */ defaults(name: string): AnimationOptions | null; + /** Which tier draws this animation. `"three"` scenes need the optional three.js. */ + rendererOf(name: string): AnimationRenderer | null; + /** + * Supply your own three.js namespace so the optional scenes never touch the + * network. Call before mounting any `"three"` animation. + */ + useThree(three: unknown): GalaxyStatic; /** Scan a DOM scope and wire all declarative components/animations. */ autoInit(scope?: Document | HTMLElement): void; diff --git a/galaxy.js b/galaxy.js index d879bcb..2087871 100644 --- a/galaxy.js +++ b/galaxy.js @@ -15,7 +15,7 @@ })(typeof self !== "undefined" ? self : this, function () { "use strict"; - var VERSION = "3.3.0"; + var VERSION = "3.4.0"; var hasDOM = typeof document !== "undefined"; var prefersReduced = hasDOM && @@ -128,20 +128,46 @@ else gl.uniform4f(loc, v[0], v[1], v[2], v[3]); } - /* A still poster painted in 2D when WebGL2 is unavailable — a shader surface - * must never degrade to an empty box. Shaders may supply a richer `fallback`. */ + /* A still poster — a GPU surface must never degrade to an empty box. + * Shaders and three.js scenes may supply a richer `fallback`. + * + * This runs in two situations, and only one of them has a 2D context: + * - the browser gave us no WebGL2 at all, so the canvas is 2D → paint it; + * - the context is alive but the content failed (a shader that would not + * compile, three.js that would not load) → the canvas is already a WebGL + * canvas and can never hand back a 2D context, so paint the *host* + * element with the equivalent CSS gradient instead. + * Reaching for h.ctx unconditionally is how this used to throw. */ function glPosterFallback(h) { + var pal = paletteOf(h.opts, ["#7c5cff", "#22d3ee"]); + var bg = h.opts.background || "#05060f"; + var painted = false; return { draw: function () { - var c = h.ctx, w = h.width, hh = h.height; - var pal = paletteOf(h.opts, ["#7c5cff", "#22d3ee"]); - c.fillStyle = h.opts.background || "#05060f"; - c.fillRect(0, 0, w, hh); - var g = c.createRadialGradient(w * 0.5, hh * 0.5, 0, w * 0.5, hh * 0.5, Math.max(w, hh) * 0.7); - g.addColorStop(0, rgba(pal[0], 0.55)); - g.addColorStop(1, rgba(pal[pal.length - 1], 0)); - c.fillStyle = g; - c.fillRect(0, 0, w, hh); + if (h.ctx) { + var c = h.ctx, w = h.width, hh = h.height; + c.fillStyle = bg; + c.fillRect(0, 0, w, hh); + var g = c.createRadialGradient(w * 0.5, hh * 0.5, 0, w * 0.5, hh * 0.5, Math.max(w, hh) * 0.7); + g.addColorStop(0, rgba(pal[0], 0.55)); + g.addColorStop(1, rgba(pal[pal.length - 1], 0)); + c.fillStyle = g; + c.fillRect(0, 0, w, hh); + return; + } + if (painted) return; // a CSS poster is static; set it once + painted = true; + h.el.style.background = + "radial-gradient(60% 60% at 50% 50%, " + rgba(pal[0], 0.55) + " 0%, " + + rgba(pal[pal.length - 1], 0) + " 100%), " + bg; + if (h.gl && !h.gl.isContextLost()) { + var b = hexToRgb(bg); + h.gl.clearColor(b[0] / 255, b[1] / 255, b[2] / 255, 0); + h.gl.clear(h.gl.COLOR_BUFFER_BIT); + } + }, + destroy: function () { + if (painted) h.el.style.background = ""; }, }; } @@ -194,6 +220,136 @@ }); } + /* ============================================================ + * three.js tier — optional, lazy, and never required. + * + * The library's contract is zero *required* dependencies, and that does not + * change here. A scene that wants a real scene graph — meshes, PBR materials, + * render targets, a post-processing chain — declares `renderer: "three"`. + * three.js is then fetched once, on demand, the first time such a scene + * mounts. A page that never uses one downloads nothing extra; a page that + * cannot reach the CDN at all still paints the 2D poster rather than an empty + * box, exactly like the WebGL2 tier degrades. + * + * To skip the network entirely, hand the library your own copy: + * import * as THREE from "three"; + * Galaxy.useThree(THREE); + * ...or point one scene somewhere else with `{ threeUrl: "/vendor/three.js" }`. + * ========================================================== */ + var THREE_URL = "https://cdn.jsdelivr.net/npm/three@0.185.1/build/three.module.min.js"; + var threeMod = null; // the resolved namespace, once we have it + var threeWait = null; // the in-flight load, shared by every scene on the page + + function loadThree(url) { + if (threeMod) return Promise.resolve(threeMod); + if (threeWait) return threeWait; + // A classic-script dynamic import: no bundler required, and nothing is + // requested until a three-tier scene is actually mounted. + threeWait = Promise.resolve() + .then(function () { return import(/* webpackIgnore: true */ url || THREE_URL); }) + .then(function (m) { threeMod = m; return m; }) + .catch(function (e) { threeWait = null; throw e; }); + return threeWait; + } + + /* Sugar over registerAnimation for a three.js scene. + * + * `scene(THREE, host)` runs only once three.js is available and returns the + * same little object every animation returns: { draw, resize?, update?, + * destroy? }. Until then — and forever, if the load fails — the poster draws, + * so `setup` can still answer synchronously like every other renderer. */ + function registerThree(name, def) { + registerAnimation(name, { + renderer: "three", + defaults: def.defaults || {}, + fallback: def.fallback || null, + setup: function (h) { + var poster = (def.fallback || glPosterFallback)(h); + var live = null; + var dead = false; + // The first resize lands before three.js arrives, so remember it. + var size = { w: h.width, h: h.height }; + + loadThree(h.opts.threeUrl) + .then(function (T) { + if (dead) return; + live = def.scene(T, h); + // Hand the surface over: drop anything the poster painted first. + if (poster.destroy) poster.destroy(); + if (live.resize) live.resize(size.w, size.h); + // Under prefers-reduced-motion the core already drew its one frame + // (the poster). Now that the real scene exists, draw its still frame. + if (h.reduced) live.draw(def.staticTime !== undefined ? def.staticTime : 0, 0); + }) + .catch(function (e) { + live = null; + if (typeof console !== "undefined") { + console.warn('GalaxyJS: "' + name + '" needs three.js; showing the still fallback.', e && e.message ? e.message : e); + } + }); + + return { + draw: function (t, dt) { + if (h.gl && h.gl.isContextLost()) return; + (live || poster).draw(t, dt); + }, + resize: function (w, hh) { + size.w = w; size.h = hh; + var target = live || poster; + if (target.resize) target.resize(w, hh); + }, + update: function (opts) { + if (live && live.update) live.update(opts); + }, + destroy: function () { + dead = true; + // Never force a context loss — see the note in the WebGL2 tier. + if (live && live.destroy) live.destroy(); + else if (poster.destroy) poster.destroy(); + live = null; + }, + }; + }, + }); + } + + /* Every three scene needs the same renderer wiring, and getting the pixel + * ratio wrong here is what makes a scene look soft. The core already sized + * the canvas to width*dpr, so setSize must not touch style or recompute it. */ + function threeRenderer(T, h) { + var r = new T.WebGLRenderer({ + canvas: h.canvas, + context: h.gl, + antialias: false, + alpha: true, + premultipliedAlpha: true, + }); + r.setPixelRatio(h.dpr); + r.setSize(h.width, h.height, false); + if ("outputColorSpace" in r) r.outputColorSpace = T.SRGBColorSpace; + r.toneMapping = T.ACESFilmicToneMapping; + r.toneMappingExposure = 1; + return r; + } + + /* Frees GPU memory for a scene graph without touching the context. */ + function threeDispose(root, extra) { + if (root && root.traverse) { + root.traverse(function (o) { + if (o.geometry && o.geometry.dispose) o.geometry.dispose(); + var m = o.material; + if (!m) return; + (Array.isArray(m) ? m : [m]).forEach(function (mm) { + for (var k in mm) { + if (mm[k] && mm[k].isTexture && mm[k].dispose) mm[k].dispose(); + } + if (mm.dispose) mm.dispose(); + }); + }); + } + (extra || []).forEach(function (o) { if (o && o.dispose) o.dispose(); }); + } + /* ============================================================ * Shared animation loop (one rAF for the whole page) * ========================================================== */ @@ -246,10 +402,11 @@ el.appendChild(canvas); var opts = Object.assign({}, def.defaults, options || {}); - // A shader animation asks for WebGL2; if the browser cannot give one, the - // surface silently becomes a 2D poster rather than an empty canvas. + // A shader or three.js animation asks for WebGL2; if the browser cannot + // give one, the surface silently becomes a 2D poster rather than an empty + // canvas. (three.js renders into this same context — see threeRenderer.) var gl = null, ctx = null, setup = def.setup; - if (def.renderer === "webgl2") { + if (def.renderer === "webgl2" || def.renderer === "three") { try { gl = canvas.getContext("webgl2", { alpha: true, antialias: false, premultipliedAlpha: true, powerPreference: "low-power", @@ -6108,6 +6265,1718 @@ }); })(); + /* ============================================================ + * three.js scenes (the optional tier) — 81-86 + * + * Everything below needs a scene graph, real volumetrics, GPU-side state or a + * post-processing chain, which is exactly what the 2D and fragment-shader + * tiers cannot give. Nothing here loads until one of them is mounted. + * ========================================================== */ + + /* 81. eventHorizon — a Schwarzschild black hole, ray-traced in curved spacetime. + * + * The 2D `blackHole` and `lensing` scenes paint what lensing looks like. This + * one solves for it. Each pixel's photon is integrated through the Schwarzschild + * metric in the orbit-plane form + * + * d²u/dφ² = -u + (3/2)·rs·u² where u = 1/r + * + * so the photon ring, the Einstein ring of the starfield behind the hole, and + * the way the far side of the disk is bent up over the top all fall out of the + * integration instead of being drawn on. The disk carries relativistic Doppler + * shift and beaming (I ∝ δ⁴), which is why one side is bright blue-white and + * the other is dim and red — the asymmetry is the physics, not a gradient. + */ + registerThree("eventHorizon", { + defaults: { + spin: 1, // how fast the disk material orbits + tilt: 0.42, // camera inclination above the disk plane, radians + diskInner: 2.6, // inner disk radius in units of rs (ISCO ≈ 3) + diskOuter: 9, + steps: 220, // integration steps per photon + exposure: 0.85, + colors: ["#fff4e2", "#ffb46b", "#7c5cff"], + background: "#03040a", + interactive: true, + }, + scene: function (T, h) { + var renderer = threeRenderer(T, h); + renderer.toneMapping = T.NoToneMapping; // the shader tonemaps itself + var scene = new T.Scene(); + var cam = new T.OrthographicCamera(-1, 1, 1, -1, 0, 1); + var pal = paletteOf(h.opts, ["#fff4e2", "#ffb46b", "#7c5cff"]); + // hexToRgb returns {r,g,b} — indexing it like an array yields NaN uniforms, + // and a NaN colour turns the whole frame white with no compile error. + var v3 = function (c) { + var f = function (x) { return Math.pow(x / 255, 2.2); }; + return new T.Vector3(f(c.r), f(c.g), f(c.b)); + }; + + var uni = { + uRes: { value: new T.Vector2(1, 1) }, + uTime: { value: 0 }, + uTilt: { value: h.opts.tilt }, + uInner: { value: h.opts.diskInner }, + uOuter: { value: h.opts.diskOuter }, + uSpin: { value: h.opts.spin }, + uSteps: { value: h.opts.steps }, + uExposure: { value: h.opts.exposure }, + uHot: { value: v3(pal[0]) }, + uWarm: { value: v3(pal[1 % pal.length]) }, + uCool: { value: v3(pal[2 % pal.length]) }, + uBg: { value: v3(hexToRgb(h.opts.background || "#03040a")) }, + }; + + var mat = new T.ShaderMaterial({ + uniforms: uni, + vertexShader: + "void main(){ gl_Position = vec4(position.xy, 0.0, 1.0); }", + fragmentShader: [ + "precision highp float;", + "uniform vec2 uRes; uniform float uTime, uTilt, uInner, uOuter, uSpin, uSteps, uExposure;", + "uniform vec3 uHot, uWarm, uCool, uBg;", + "out vec4 fragColor;", + + "float hash(vec3 p){ return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453); }", + "float noise(vec3 p){", + " vec3 i = floor(p), f = fract(p); f = f*f*(3.0-2.0*f);", + " float a = mix(mix(mix(hash(i), hash(i+vec3(1,0,0)), f.x), mix(hash(i+vec3(0,1,0)), hash(i+vec3(1,1,0)), f.x), f.y),", + " mix(mix(hash(i+vec3(0,0,1)), hash(i+vec3(1,0,1)), f.x), mix(hash(i+vec3(0,1,1)), hash(i+vec3(1,1,1)), f.x), f.y), f.z);", + " return a;", + "}", + "float fbm(vec3 p){ float s=0.0, a=0.5; for(int i=0;i<4;i++){ s+=a*noise(p); p*=2.03; a*=0.5; } return s; }", + + /* The starfield the hole lenses. Sampled by direction, so a bent ray + * genuinely looks somewhere else in the sky. */ + /* Stars as actual points on a spherical grid, not lit grid cells. + * Hashing floor(direction*k) paints cube-shaped cells, and because a + * lensed ray sweeps slowly along some loci it draws them as dotted + * straight lines across the sky. Placing one star at a random offset + * inside each cell with a smooth radial falloff removes the artifact + * and gives round stars that survive magnification near the ring. */ + "vec3 sky(vec3 d){", + " vec3 c = uBg;", + " float lon = atan(d.z, d.x);", + " float lat = asin(clamp(d.y, -1.0, 1.0));", + " vec2 sc = vec2(lon * 0.1591549, lat * 0.3183099) * 150.0;", // lon/2pi, lat/pi + " vec2 cell = floor(sc), frc = fract(sc);", + " for (int oy = -1; oy <= 1; oy++) {", + " for (int ox = -1; ox <= 1; ox++) {", + " vec2 o = vec2(float(ox), float(oy));", + " vec2 id = cell + o;", + " float r1 = hash(vec3(id, 1.0));", + " if (r1 < 0.90) continue;", // ~10% of cells hold a star + " vec2 pos = o + vec2(hash(vec3(id, 7.0)), hash(vec3(id, 13.0)));", + " float dist = length(frc - pos);", + " float mag = (r1 - 0.90) * 10.0;", // 0..1 brightness draw + " float tw = 0.72 + 0.28 * sin(uTime * 1.9 + r1 * 90.0);", + " vec3 tint = mix(vec3(0.62, 0.74, 1.0), vec3(1.0, 0.82, 0.62), hash(vec3(id, 21.0)));", + " c += tint * pow(mag, 3.0) * tw * exp(-dist * dist * 190.0);", + " }", + " }", + // a faint galactic band, so lensing has large-scale structure to bend + " float dy = d.y * 7.0;", + " float band = exp(-dy * dy) * (0.012 + 0.05 * fbm(d * 7.0));", + " c += uCool * band * 0.35;", + " return c;", + "}", + + /* Disk emission: temperature rises inward (T ∝ r^-3/4), turbulence in + * the orbiting frame, and a sharp inner cut. */ + "vec3 disk(float r, float phi){", + " float x = clamp((r - uInner) / max(0.001, uOuter - uInner), 0.0, 1.0);", + " float temp = pow(uInner / r, 0.75);", + " float orbit = uTime * uSpin * 34.0 / pow(r, 1.5);", // Keplerian shear + " float turb = fbm(vec3(cos(phi + orbit) * r, sin(phi + orbit) * r, r * 0.4) * 0.9);", + " float dens = smoothstep(0.0, 0.09, x) * (1.0 - smoothstep(0.30, 0.95, x));", + " dens *= 0.30 + 1.05 * turb * turb;", + " vec3 col = mix(uWarm, uHot, clamp(temp * 1.25, 0.0, 1.0));", + " return col * dens * (0.85 * temp * temp);", + "}", + + "void main(){", + " vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;", + // camera: looking at the hole from above the disk plane + " float ct = cos(uTilt), st = sin(uTilt);", + " vec3 eye = vec3(0.0, st, -ct) * 22.0;", + " vec3 fwd = normalize(-eye);", + " vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), fwd));", + " vec3 up = cross(fwd, right);", + " vec3 dir = normalize(fwd * 2.4 + right * uv.x + up * uv.y);", + + /* Integrate in the plane spanned by (eye, dir). Everything below is + * 2D in that plane, which is what makes a real geodesic affordable. */ + " vec3 nrm = normalize(cross(eye, dir));", // orbital plane normal + " vec3 e1 = normalize(eye);", + " vec3 e2 = normalize(cross(nrm, e1));", + " float r = length(eye);", + " float u = 1.0 / r;", + // du/dphi from the projection of the ray direction onto the plane basis + " float dr = dot(dir, e1);", + " float rdphi = dot(dir, e2);", + " float du = -u * dr / max(1e-4, rdphi);", // u' = -(1/r²)(dr/dphi) + " float phi0 = 0.0;", + " float dphi = 3.2 / uSteps;", // sweep up to ~pi + " vec3 acc = vec3(0.0);", + " bool captured = false;", + " float prevY = dot(eye, vec3(0.0, 1.0, 0.0));", + " vec3 pos = eye;", + + " for (int i = 0; i < 512; i++) {", + " if (float(i) >= uSteps) break;", + // RK2 on u'' = -u + 1.5*rs*u², rs = 1 + " float k1 = -u + 1.5 * u * u;", + " float uMid = u + 0.5 * dphi * du;", + " float duMid = du + 0.5 * dphi * k1;", + " float k2 = -uMid + 1.5 * uMid * uMid;", + " u += dphi * duMid;", + " du += dphi * k2;", + " phi0 += dphi;", + " if (u <= 0.0) break;", // escaped to infinity + " if (u > 1.0) { captured = true; break; }", // crossed the horizon r < rs + " float rr = 1.0 / u;", + " vec3 next = (e1 * cos(phi0) + e2 * sin(phi0)) * rr;", + " float y = next.y;", + // disk crossing: sign change of y between steps, interpolated + " if (prevY * y < 0.0) {", + " float f = prevY / (prevY - y);", + " vec3 hit = mix(pos, next, f);", + " float hr = length(hit.xz);", + " if (hr > uInner && hr < uOuter) {", + " float ph = atan(hit.z, hit.x);", + // Doppler: orbital velocity of the material, projected on the line of sight + " vec3 vdir = normalize(vec3(-hit.z, 0.0, hit.x));", + " float beta = min(0.78, 1.02 / sqrt(max(1.2, hr)));", + " vec3 look = normalize(hit - eye);", + " float mu = dot(vdir, -look);", + " float g = 1.0 / sqrt(1.0 - beta * beta);", + " float delta = 1.0 / (g * (1.0 - beta * mu));", // Doppler factor + " float beam = pow(delta, 4.0);", // relativistic beaming + " vec3 em = disk(hr, ph) * beam;", + // blueshift the approaching side, redden the receding side + " em *= mix(vec3(1.0, 0.72, 0.5), vec3(0.72, 0.86, 1.0), clamp(delta * 0.5, 0.0, 1.0));", + " acc += em;", + " }", + " }", + " prevY = y; pos = next;", + " }", + + " vec3 col = acc;", + " if (!captured) {", + /* The outgoing direction is the full geodesic tangent: + * d/dphi [ r(phi) * rhat ] = r' * rhat + r * phihat, r' = -u'/u^2 + * Using only the phihat term (the obvious mistake) leaves the far + * field a non-identity map, which smears background stars into long + * straight streaks right across the frame. */ + " vec3 rhat = e1 * cos(phi0) + e2 * sin(phi0);", + " vec3 phat = -e1 * sin(phi0) + e2 * cos(phi0);", + " vec3 tangent = normalize(rhat * (-du / max(1e-6, u)) + phat);", + " col += sky(tangent);", + " }", + " col *= uExposure;", + " col = col / (1.0 + col);", // Reinhard + " col = pow(col, vec3(1.0 / 2.2));", + " fragColor = vec4(col, 1.0);", + "}", + ].join("\n"), + glslVersion: T.GLSL3, + }); + + var quad = new T.Mesh(new T.PlaneGeometry(2, 2), mat); + quad.frustumCulled = false; + scene.add(quad); + + return { + resize: function (w, hh) { + renderer.setPixelRatio(h.dpr); + renderer.setSize(w, hh, false); + uni.uRes.value.set(h.canvas.width, h.canvas.height); + }, + update: function (o) { + uni.uTilt.value = o.tilt; + uni.uInner.value = o.diskInner; + uni.uOuter.value = o.diskOuter; + uni.uSpin.value = o.spin; + uni.uExposure.value = o.exposure; + }, + draw: function (t) { + uni.uTime.value = t; + // the pointer leans the camera, so the lensing asymmetry is explorable + if (h.mouse.active) { + var ny = (h.mouse.y / Math.max(1, h.height)) - 0.5; + uni.uTilt.value += (h.opts.tilt - ny * 0.9 - uni.uTilt.value) * 0.06; + } else { + uni.uTilt.value += (h.opts.tilt - uni.uTilt.value) * 0.04; + } + renderer.render(scene, cam); + }, + destroy: function () { threeDispose(scene, [mat, renderer]); }, + }; + }, + }); + + /* molecularCloud — a real single-scattering volume raymarch. + * + * A 128^3 R8 Data3DTexture is filled on the CPU with curl-warped multiplicative + * ridged noise plus two dense cores, then marched inside a box in the box's own + * local space: entry/exit come from a slab intersection of the unit AABB, every + * sample takes a 6-step shadow march toward the embedded protostar, and the result + * composites with Beer-Lambert extinction and a Henyey-Greenstein phase function. + * Because the march happens in the mesh's local space, rotating the mesh rotates + * the volume *and* its star field: near filaments genuinely slide in front of far + * ones. No 2D fragment shader can produce that parallax. + */ + registerThree("molecularCloud", { + defaults: { + colors: ["#ffb37a", "#78aaff", "#c58cff"], + background: "#04060e", + res: 128, // 3D texture edge (128^3 = 2.1M voxels) + steps: 64, // primary march samples + density: 9.0, // extinction coefficient + shadow: 2.60, // absorption along the light march + emission: 1.35, // scatter gain + forward: 0.62, // Henyey-Greenstein g + spin: 0.13, // rad/s of volume rotation + churn: 0.30, // amplitude of the internal shear + distance: 2.02, + }, + + scene: function (T, h) { + var renderer = threeRenderer(T, h); + var o = h.opts; + var pal = paletteOf(o, ["#ffb37a", "#78aaff", "#c58cff"]); + function lin(c) { // sRGB byte -> linear float triple + return [Math.pow(c.r / 255, 2.2), Math.pow(c.g / 255, 2.2), Math.pow(c.b / 255, 2.2)]; + } + function v3(a) { return new T.Vector3(a[0], a[1], a[2]); } + var bg = lin(hexToRgb(o.background || "#04060e")); + + /* ---- CPU volume -------------------------------------------------- */ + var N = Math.max(48, Math.min(128, o.res | 0)); + var G = 64, GM = G - 1, GS = 6; // tiling value-noise lattice + var lat = new Float32Array(G * G * G), sd = 1337; + for (var i = 0; i < lat.length; i++) { + sd = (Math.imul(sd, 1664525) + 1013904223) >>> 0; + lat[i] = sd / 4294967296; + } + // Lattice fetch wrapped to period m+1 then shifted, so every octave tiles + // exactly over the volume and no octave shows a seam at the box faces. + function gv(x, y, z, m, s) { + return lat[((((x & m) + s) & GM) << (GS * 2)) | ((((y & m) + s * 3) & GM) << GS) | (((z & m) + s * 7) & GM)]; + } + function vn(x, y, z, m, s) { // trilinear value noise, period m+1 + var xi = Math.floor(x), yi = Math.floor(y), zi = Math.floor(z); + var fx = x - xi, fy = y - yi, fz = z - zi; + fx = fx * fx * (3 - 2 * fx); fy = fy * fy * (3 - 2 * fy); fz = fz * fz * (3 - 2 * fz); + var a = gv(xi, yi, zi, m, s), b = gv(xi + 1, yi, zi, m, s); + var c = gv(xi, yi + 1, zi, m, s), d = gv(xi + 1, yi + 1, zi, m, s); + var e = gv(xi, yi, zi + 1, m, s), f = gv(xi + 1, yi, zi + 1, m, s); + var g = gv(xi, yi + 1, zi + 1, m, s), k = gv(xi + 1, yi + 1, zi + 1, m, s); + var x0 = a + (b - a) * fx, x1 = c + (d - c) * fx; + var x2 = e + (f - e) * fx, x3 = g + (k - g) * fx; + var l0 = x0 + (x1 - x0) * fy, l1 = x2 + (x3 - x2) * fy; + return l0 + (l1 - l0) * fz; + } + // curl of a low-frequency vector potential, on a coarse grid we interpolate + var C = 24, curl = new Float32Array(C * C * C * 3), eps = 0.30; + function pot(x, y, z, s) { return vn(x, y, z, 3, s * 5 + 2); } + for (var cz = 0; cz < C; cz++) for (var cy = 0; cy < C; cy++) for (var cx = 0; cx < C; cx++) { + var gx = (cx / C) * 4, gy = (cy / C) * 4, gz = (cz / C) * 4; + var dP3y = pot(gx, gy + eps, gz, 3) - pot(gx, gy - eps, gz, 3); + var dP2z = pot(gx, gy, gz + eps, 2) - pot(gx, gy, gz - eps, 2); + var dP1z = pot(gx, gy, gz + eps, 1) - pot(gx, gy, gz - eps, 1); + var dP3x = pot(gx + eps, gy, gz, 3) - pot(gx - eps, gy, gz, 3); + var dP2x = pot(gx + eps, gy, gz, 2) - pot(gx - eps, gy, gz, 2); + var dP1y = pot(gx, gy + eps, gz, 1) - pot(gx, gy - eps, gz, 1), ci = (cz * C * C + cy * C + cx) * 3; + curl[ci] = (dP3y - dP2z) / eps; curl[ci + 1] = (dP1z - dP3x) / eps; curl[ci + 2] = (dP2x - dP1y) / eps; + } + function curlAt(u, v, w, comp) { // nearest-ish coarse fetch, cheap + var ix = ((u * C) | 0) % C, iy = ((v * C) | 0) % C, iz = ((w * C) | 0) % C; + return curl[((iz * C * C + iy * C + ix) * 3) + comp]; + } + var cores = [[0.12, -0.04, 0.05, 0.17, 0.85], [-0.36, 0.24, -0.30, 0.12, 0.55]]; + var data = new Uint8Array(N * N * N); + var warpAmp = 0.55, ptr = 0; + for (var z = 0; z < N; z++) for (var y = 0; y < N; y++) for (var x = 0; x < N; x++) { + var u = x / N, v = y / N, w = z / N; + var qx = u + curlAt(u, v, w, 0) * warpAmp; + var qy = v + curlAt(u, v, w, 1) * warpAmp; + var qz = w + curlAt(u, v, w, 2) * warpAmp; + // Multiplicative ridges: a voxel is only dense where every octave's ridge + // agrees, which is what leaves thin branching filaments and empty voids. + var fil = 1; + for (var oc = 0; oc < 5; oc++) { // periods 4/8/16/32/64 (powers of two: the wrap is a mask) + var per = 4 << oc; + var wt = 0.30 + 0.14 * oc; // fine octaves only modulate, so + var n = vn(qx * per, qy * per, qz * per, per - 1, oc * 5 + 1); + fil *= wt + (1 - wt) * (1 - Math.abs(n * 2 - 1)); // the big shapes survive + } + // hard-ish transfer function: sharp filament boundaries read as structure, + // a smooth ramp just reads as haze + fil = (fil - 0.42) / 0.20; if (fil < 0) fil = 0; if (fil > 1) fil = 1; + fil = fil * fil * (3 - 2 * fil); + var env = vn(u * 4, v * 4, w * 4, 3, 19); // big clouds and voids + env = (env - 0.30) * 2.3; if (env < 0) env = 0; if (env > 1) env = 1; + var dv = fil * (0.10 + 1.15 * env); + var px = u * 2 - 1, py = v * 2 - 1, pz = w * 2 - 1; + // irregular envelope, so the silhouette is not a smooth ellipsoid + var rr = Math.sqrt(px * px + py * py * 1.80 + pz * pz); + var lowN = vn(u * 4 + 0.5, v * 4 + 0.5, w * 4 + 0.5, 3, 41) * 0.68 + + vn(u * 8 + 0.5, v * 8 + 0.5, w * 8 + 0.5, 7, 53) * 0.32; + var sh = (1.00 - rr + 0.52 * (lowN - 0.5)) / 0.42; + if (sh < 0) sh = 0; if (sh > 1) sh = 1; + dv *= sh * sh * (3 - 2 * sh); + for (var ci2 = 0; ci2 < cores.length; ci2++) { + var cc = cores[ci2]; + var ddx = px - cc[0], ddy = py - cc[1], ddz = pz - cc[2]; + dv += cc[4] * Math.exp(-(ddx * ddx + ddy * ddy + ddz * ddz) / (cc[3] * cc[3])); + } + data[ptr++] = dv > 1 ? 255 : (dv * 255) | 0; + } + var vol = new T.Data3DTexture(data, N, N, N); + vol.format = T.RedFormat; vol.type = T.UnsignedByteType; + vol.minFilter = T.LinearFilter; vol.magFilter = T.LinearFilter; + vol.wrapS = vol.wrapT = vol.wrapR = T.RepeatWrapping; + vol.unpackAlignment = 1; vol.needsUpdate = true; + + /* ---- shader ------------------------------------------------------ */ + var VERT = ["out vec3 vLocal;", + "void main(){ vLocal = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }"].join("\n"); + + var FRAG = [ + "precision highp float;", "precision highp sampler3D;", + "uniform sampler3D uVol;", + "uniform vec3 uCam, uStar, uBg, uWarm, uCool, uRim;", + "uniform float uDens, uShadow, uEmit, uG, uSteps, uTwist;", + "in vec3 vLocal;", "out vec4 outColor;", + "float h21(vec2 p){ return fract(sin(dot(p, vec2(12.9898,78.233)))*43758.5453); }", + "float h31(vec3 p){ return fract(sin(dot(p, vec3(12.9898,78.233,37.719)))*43758.5453); }", + "float dens(vec3 p){", + " float r = length(p * vec3(1.0,1.34,1.0));", + " if (r > 1.04) return 0.0;", // cheap early-out; the envelope is baked in + " float a = uTwist * (1.0 - r*0.55);", // differential shear: the cloud churns + " vec3 q = vec3(p.x - a*p.z, p.y, p.z + a*p.x);", + " return texture(uVol, q*0.5 + 0.5).r * smoothstep(1.04, 0.96, r);", + "}", + "float hg(float c, float g){ float d = 1.0 + g*g - 2.0*g*c; return (1.0-g*g)/(12.566371*pow(max(d,1e-4),1.5)); }", + "vec3 sky(vec3 d){", + " vec3 c = uBg * (0.60 + 0.85*(d.y*0.5+0.5)) + uCool*0.0085 + uRim*0.0035;", + " for (int L=0; L<2; L++){", + " float sc = L==0 ? 27.0 : 47.0;", + " vec3 g = d*sc; vec3 cel = floor(g); vec3 f = g - cel;", + " float br = h31(cel + float(L)*3.7);", + " if (br > 0.60){", + " vec3 off = clamp(vec3(h31(cel+11.7), h31(cel+23.3), h31(cel+31.9)), 0.10, 0.90);", + " float dd = length(f - off);", + " float s = exp(-dd*dd*220.0) * (br-0.60)*2.6 / sc * 32.0;", + " c += s * mix(vec3(0.62,0.74,1.0), vec3(1.0,0.86,0.66), h31(cel+7.3));", + " }", + " }", + " return c;", + "}", + "void main(){", + " vec3 ro = uCam;", + " vec3 rd = normalize(vLocal - uCam);", + " vec3 iv = 1.0/rd;", + " vec3 t0 = (vec3(-1.0)-ro)*iv, t1 = (vec3(1.0)-ro)*iv;", + " vec3 tn = min(t0,t1), tf = max(t0,t1);", + " float tE = max(max(tn.x,tn.y),tn.z), tX = min(min(tf.x,tf.y),tf.z);", + " vec3 col = sky(rd);", + " tE = max(tE, 0.0);", + " if (tX > tE){", + " int NS = int(uSteps);", + " float dt = (tX-tE)/float(NS);", + " float t = tE + dt*h21(gl_FragCoord.xy);", + " float tr = 1.0; vec3 acc = vec3(0.0);", + " for (int i=0; i<96; i++){", + " if (i>=NS || tr<0.012) break;", + " vec3 p = ro + rd*t;", + " float d = dens(p);", + " if (d > 0.004){", + " vec3 tos = uStar - p; float sdist = length(tos);", + " vec3 ld = tos/max(sdist,1e-3);", + " float ls = min(sdist, 0.60)/6.0; float tau = 0.0;", + " for (int j=0; j<6; j++) tau += dens(p + ld*(ls*(float(j)+0.5)));", + " float sh = exp(-tau*ls*uShadow*uDens);", + " float ph = hg(dot(rd, ld), uG);", + " float fall = min(1.0/(0.10 + sdist*sdist*2.6), 5.0);", + " vec3 tint = mix(uWarm, mix(uRim, uCool, 0.62)*0.52, clamp((sdist-0.06)*0.92, 0.0, 1.0));", + " tint *= mix(vec3(1.0), vec3(1.30,0.84,0.58), (1.0-sh)*exp(-sdist*1.6));", // dust reddens what gets through + " vec3 amb = (uCool*0.80 + uRim*0.20) * exp(-d*3.2) * 0.017;", + " vec3 rad = tint*sh*fall*(0.30 + 9.0*ph) + amb;", + " float a = 1.0 - exp(-d*uDens*dt);", + " acc += tr * a * rad * uEmit;", + " tr *= 1.0 - a;", + " }", + " vec3 sv = p - uStar; float r2 = dot(sv,sv);", + " acc += tr * uWarm * (44.0/(1.0 + r2*r2*300000.0)) * dt;", + " t += dt;", + " }", + " col = col*tr + acc;", + " }", + " col = (col*(2.10*col + 0.06)) / (col*(2.10*col + 0.90) + 0.16);", + " outColor = vec4(pow(max(col, 0.0), vec3(1.0/2.2)), 1.0);", + "}"].join("\n"); + + var U = { + uVol: { value: vol }, + uCam: { value: new T.Vector3() }, + uStar: { value: new T.Vector3(0.10, -0.05, 0.06) }, + uBg: { value: v3(bg) }, + uWarm: { value: v3(lin(pal[0])) }, + uCool: { value: v3(lin(pal[1 % pal.length])) }, + uRim: { value: v3(lin(pal[2 % pal.length])) }, + uDens: { value: o.density }, uShadow: { value: o.shadow }, + uEmit: { value: o.emission }, uG: { value: o.forward }, + uSteps: { value: Math.max(16, Math.min(96, o.steps | 0)) }, + uTwist: { value: 0 }, + }; + var mat = new T.ShaderMaterial({ + glslVersion: T.GLSL3, uniforms: U, vertexShader: VERT, fragmentShader: FRAG, + side: T.BackSide, depthWrite: false, depthTest: false, + }); + var scene = new T.Scene(); + var box = new T.Mesh(new T.BoxGeometry(18, 18, 18), mat); + box.frustumCulled = false; + scene.add(box); + + var cam = new T.PerspectiveCamera(46, h.width / Math.max(1, h.height), 0.05, 200); + var camLocal = new T.Vector3(), origin = new T.Vector3(); + var dist = o.distance, az = 0, el = 0; + + function place(t) { + var mx = h.mouse.active ? (h.mouse.x / Math.max(1, h.width)) * 2 - 1 : 0; + var my = h.mouse.active ? (h.mouse.y / Math.max(1, h.height)) * 2 - 1 : 0; + az += (mx * 0.55 - az) * 0.06; el += (-my * 0.38 - el) * 0.06; + cam.position.set( + Math.sin(az) * Math.cos(el) * dist, + Math.sin(el) * dist, + Math.cos(az) * Math.cos(el) * dist + ); + cam.lookAt(origin); + box.rotation.set(0.22 + t * o.spin * 0.31, 0.60 + t * o.spin, t * o.spin * 0.14); + box.updateMatrixWorld(true); + camLocal.copy(cam.position); box.worldToLocal(camLocal); + U.uCam.value.copy(camLocal); + U.uTwist.value = o.churn * Math.sin(t * 0.23) + o.churn * 0.5 * Math.sin(t * 0.41 + 1.0); + } + place(0); + + return { + resize: function (w, hh) { + renderer.setPixelRatio(h.dpr); renderer.setSize(w, hh, false); + cam.aspect = w / Math.max(1, hh); cam.updateProjectionMatrix(); + }, + update: function (n) { + if (n.density != null) U.uDens.value = n.density; + if (n.emission != null) U.uEmit.value = n.emission; + if (n.forward != null) U.uG.value = n.forward; + if (n.shadow != null) U.uShadow.value = n.shadow; + if (n.distance != null) dist = n.distance; + if (n.spin != null) o.spin = n.spin; + }, + draw: function (t) { place(h.reduced ? 0 : t); renderer.render(scene, cam); }, + destroy: function () { threeDispose(scene, [vol, mat, renderer]); }, + }; + }, + }); + + /* ============================================================ + * spiralForge — a 340,000-star spiral galaxy in one draw call. + * + * WHY THIS NEEDS THREE.JS: it is a single T.Points over a BufferGeometry of + * ~340k real stars, each an independent body with its own orbital radius, + * phase, scale height, mass and photospheric temperature. A fullscreen + * fragment shader can fake a smear of arms; it cannot give you a third of a + * million individually-coloured, individually-orbiting point masses seen + * through a perspective camera with correct 1/w foreshortening. + * + * NO CPU WORK PER FRAME. The per-star attributes are baked exactly once in + * scene(). draw() touches four uniforms and the camera transform — there is + * no JavaScript loop over stars anywhere in the animation loop. Each star's + * orbital angle is integrated entirely in the VERTEX SHADER as + * angle = phase + uTime * omega(r), omega(r) = v0 / (r + rc) + * which is a real flat rotation curve: v(r) = v0 * r/(r+rc) is constant + * outside the core, so omega falls as 1/r. Consequence, and it is the correct + * one: the logarithmic-spiral arms baked into the phase distribution are a + * density-wave *pattern the stars drift through*, not a rigid structure. + * Because omega varies with radius the pattern shears and winds up over + * time. That is visible, and it is physics, not a bug. + * ========================================================== */ + registerThree("spiralForge", { + defaults: { + stars: 340000, // individual point masses (clamped to 60k..500k) + arms: 2, // density-wave arms (2 = grand design) + pitch: 0.50, // log-spiral pitch, radians + spin: 1, // time scale on the rotation curve + bulge: 0.26, // fraction of stars in the spheroidal old bulge + thickness: 1, // thin-disk scale-height multiplier + tilt: 36, // camera elevation above the disk plane, degrees + size: 1, + tint: 0.34, // how far the palette pulls the blackbody colours + colors: ["#ffc98a", "#fff4e6", "#a8c8ff"], + background: "#03040c", + }, + scene: function (T, h) { + var renderer = threeRenderer(T, h); + var o = h.opts; + var pal = paletteOf(o, ["#ffc98a", "#fff4e6", "#a8c8ff"]); + function pc(i) { var c = pal[i % pal.length]; return new T.Vector3(c.r / 255, c.g / 255, c.b / 255); } + + var scene = new T.Scene(); + var FOV = 40; + var cam = new T.PerspectiveCamera(FOV, Math.max(0.2, h.width / h.height), 0.05, 60); + renderer.setClearColor(new T.Color(o.background || "#03040c"), 1); + + /* ---- one-time CPU bake of the stellar population ------------------ */ + var N = Math.max(60000, Math.min(500000, Math.round(o.stars || 340000))); + var ARMS = Math.max(1, Math.round(o.arms || 2)); + var TANP = Math.tan(Math.max(0.08, Math.min(0.9, o.pitch || 0.5))); + var HR = 0.95, RMAX = 3.0, RB = 0.085; + var THK = Math.max(0.15, o.thickness || 1); + var BF = Math.max(0, Math.min(0.6, o.bulge === undefined ? 0.26 : o.bulge)); + var SZ = Math.max(0.2, o.size || 1); + + var seed = 0x51ea2f1; + function rnd() { seed = (seed * 1664525 + 1013904223) >>> 0; return (seed >>> 8) / 16777216; } + function ln() { return -Math.log(1e-7 + rnd()); } + function sat(x) { return x < 0 ? 0 : x > 1 ? 1 : x; } + + var pos = new Float32Array(N * 3); // packed (radius, height, phase) + var st = new Float32Array(N * 3); // packed (worldSize, temp01, intensity) + var i, r, y, ph, temp, dim, u; + for (i = 0; i < N; i++) { + if (rnd() < 0.07) { + // Metal-poor stellar halo: a sparse spheroid well outside the disk, + // old and red, on strongly inclined orbits. + r = 1.1 + 3.4 * Math.pow(rnd(), 1.7); + y = 0.75 * r * (rnd() + rnd() + rnd() - 1.5) * 1.2; + temp = 0.04 + 0.26 * Math.pow(rnd(), 1.6); + dim = 0.75; + ph = rnd() * Math.PI * 2; + } else if (rnd() < BF) { + // Spheroidal bulge: 3D exponential density, rho ~ exp(-r/RB), so the + // radius is Gamma(3) distributed. Flattened slightly onto the disk. + r = RB * (ln() + ln() + ln()); + if (r > 1.3) r = 1.3 * rnd(); + // The innermost few percent are the nuclear star cluster — a real + // component, and it keeps the projected centre from thinning out. + if (rnd() < 0.10) r *= 0.35; + y = 0.42 * r * (rnd() * 2 - 1); + // Age/metallicity gradient: the packed centre is whiter, the outer + // spheroid is the classic red old population. + temp = 0.12 + 0.32 * Math.pow(rnd(), 1.3) + 0.22 * sat(1.0 - r / 0.35); + dim = 0.72; + ph = rnd() * Math.PI * 2; + } else { + // Exponential thin disk: surface density Sigma ~ exp(-r/HR) means the + // radial PDF is r*exp(-r/HR), i.e. Gamma(2) — sampled exactly. + r = HR * (ln() + ln()); + while (r > RMAX) r = HR * (ln() + ln()); // rejection, keeps the profile exact + var hz = (0.028 + 0.014 * r) * THK; // gentle outward flare + y = hz * (rnd() + rnd() + rnd() - 1.5) * 1.7; + // Density-wave arms: pile the initial phase up on a logarithmic + // spiral theta = ln(r/r0)/tan(pitch), with a scatter that widens + // outward. Arms fade out inside the bar region and past the disk edge. + var af = sat((r - 0.3) / 0.45) * sat((RMAX + 0.5 - r) / 1.1); + var ridge = Math.log(Math.max(r, 0.1) / 0.26) / TANP; + var sep = (Math.PI * 2) / ARMS; + var w = 0.34 + 0.46 * (r / RMAX); + if (rnd() < 0.44 * af) { + var g = (rnd() + rnd() + rnd() - 1.5) * 1.15 * w; + ph = ridge + sep * Math.floor(rnd() * ARMS) + g; + // Young population: arms are where gas collapses, so this is where + // the rare, short-lived, hot O/B stars live. + u = rnd(); + if (u < 0.05) temp = 0.82 + 0.18 * rnd(); + else if (u < 0.21) temp = 0.60 + 0.20 * rnd(); + else temp = 0.18 + 0.40 * rnd(); + dim = 0.88; + } else { + ph = rnd() * Math.PI * 2; + temp = 0.14 + 0.44 * Math.pow(rnd(), 1.15); + dim = 0.68; + } + // Dust lanes. Signed angular offset from the nearest arm ridge, in + // units of the arm width; a narrow band on the concave side of each + // arm is where the molecular gas and dust sit, so stars behind it are + // extinguished. This is the dark thread that makes an arm read as an + // arm rather than a stripe. + var dd = ph - ridge; + dd -= Math.round(dd / sep) * sep; + var e = dd / w; + if (e > 0.32 && e < 0.80) dim *= 1.0 - 0.55 * af; + // Smooth outer truncation so the disk fades away instead of ending. + var tap = sat((RMAX - r) / 1.0); + dim *= 0.25 + 0.75 * tap * tap; + } + // Mass-temperature relation for the main sequence, then the + // mass-luminosity relation L ~ M^3.5. The dynamic range is 10^7, so it + // is compressed by fractional powers — apparent radius ~ L^0.10 and + // surface brightness ~ L^0.17 — which still leaves the hot blue giants + // several times bigger and brighter than the red dwarfs around them. + var mass = 0.18 * Math.pow(10, 2.05 * temp); + var lum = Math.pow(mass, 3.5); + pos[i * 3] = r; pos[i * 3 + 1] = y; pos[i * 3 + 2] = ph; + st[i * 3] = 0.0112 * SZ * (0.85 + 1.95 * Math.pow(lum, 0.10)); + st[i * 3 + 1] = temp; + st[i * 3 + 2] = dim * (0.020 + 0.115 * Math.pow(lum, 0.17)); + } + + var geo = new T.BufferGeometry(); + geo.setAttribute("position", new T.BufferAttribute(pos, 3)); + geo.setAttribute("aStar", new T.BufferAttribute(st, 3)); + geo.boundingSphere = new T.Sphere(new T.Vector3(0, 0, 0), 6.0); + + var mat = new T.ShaderMaterial({ + uniforms: { + uTime: { value: 0 }, + uSpin: { value: 0.1 * (o.spin === undefined ? 1 : o.spin) }, + uCore: { value: 0.4 }, + uPx: { value: 500 }, + uTint: { value: Math.max(0, Math.min(1, o.tint === undefined ? 0.34 : o.tint)) }, + uCool: { value: pc(0) }, uWarm: { value: pc(1) }, uHot: { value: pc(2) }, + }, + vertexShader: [ + "attribute vec3 aStar;", + "uniform float uTime, uSpin, uCore, uPx, uTint;", + "uniform vec3 uCool, uWarm, uHot;", + "varying vec3 vCol; varying float vI;", + // Blackbody locus, ~2500 K red -> 5800 K white -> 20000 K blue. + "vec3 bb(float t){", + " vec3 a=vec3(1.00,0.36,0.09), b=vec3(1.00,0.64,0.33), c=vec3(1.00,0.93,0.86);", + " vec3 d=vec3(0.80,0.86,1.00), e=vec3(0.60,0.71,1.00);", + " if(t<0.30) return mix(a,b,t/0.30);", + " if(t<0.58) return mix(b,c,(t-0.30)/0.28);", + " if(t<0.82) return mix(c,d,(t-0.58)/0.24);", + " return mix(d,e,(t-0.82)/0.18);", + "}", + "void main(){", + " float r = position.x, hgt = position.y, ph = position.z;", + // Flat rotation curve: v(r)=v0*r/(r+rc) -> omega=v/r=v0/(r+rc). + " float ang = ph + uTime * (uSpin / (r + uCore));", + " vec3 p = vec3(cos(ang)*r, hgt, sin(ang)*r);", + " vec4 mv = modelViewMatrix * vec4(p, 1.0);", + " gl_Position = projectionMatrix * mv;", + " float w = max(gl_Position.w, 0.02);", + " float px = aStar.x * uPx / w;", // perspective 1/w scaling + " gl_PointSize = clamp(px, 1.0, 16.0);", + " float sub = min(px, 1.0);", // sub-pixel stars dim, not shrink + " vec3 t = bb(aStar.y);", + " vec3 q = aStar.y < 0.5 ? mix(uCool, uWarm, aStar.y*2.0)", + " : mix(uWarm, uHot, (aStar.y-0.5)*2.0);", + " vCol = mix(t, t*0.35 + q*0.8, uTint);", + " vI = aStar.z * sub * sub;", + "}", + ].join("\n"), + fragmentShader: [ + "precision mediump float;", + "varying vec3 vCol; varying float vI;", + "void main(){", + " vec2 q = gl_PointCoord - 0.5;", + " float d2 = dot(q, q);", + " if (d2 > 0.25) discard;", // round stars, never square + " float a = 1.0 - d2 * 4.0;", + " a = a * a * (0.35 + 0.65 * a);", // soft airy-ish core + " gl_FragColor = vec4(vCol * vI, a * vI);", + "}", + ].join("\n"), + blending: T.AdditiveBlending, + depthTest: false, + depthWrite: false, + transparent: true, + }); + + var stars = new T.Points(geo, mat); + stars.frustumCulled = false; + scene.add(stars); + + var az = 0.65; + var el = (Math.max(6, Math.min(80, o.tilt || 36)) * Math.PI) / 180; + var elT = el; + function place() { + var R = 3.9; + cam.position.set(R * Math.cos(el) * Math.cos(az), R * Math.sin(el), R * Math.cos(el) * Math.sin(az)); + cam.lookAt(0, -0.16, 0); + } + place(); + + return { + resize: function (w, hh) { + renderer.setPixelRatio(h.dpr); + renderer.setSize(w, hh, false); + cam.aspect = Math.max(0.2, w / hh); + cam.updateProjectionMatrix(); + // gl_PointSize is in framebuffer pixels, so DPR belongs in here. + mat.uniforms.uPx.value = 0.5 * hh * h.dpr / Math.tan((FOV * 0.5 * Math.PI) / 180); + }, + update: function (op) { + if (op.spin !== undefined) mat.uniforms.uSpin.value = 0.1 * op.spin; + if (op.tint !== undefined) mat.uniforms.uTint.value = op.tint; + if (op.background) renderer.setClearColor(new T.Color(op.background), 1); + if (op.tilt !== undefined) elT = (op.tilt * Math.PI) / 180; + }, + draw: function (t, dt) { + mat.uniforms.uTime.value = t; + az = 0.65 + t * 0.035; // slow azimuthal drift + if (h.mouse && h.mouse.active) { + // Mouse height sweeps the view from near face-on to near edge-on. + var my = Math.max(0, Math.min(1, h.mouse.y / Math.max(1, h.height))); + elT = 1.18 + (0.06 - 1.18) * my; + } + var k = Math.min(1, (dt || 0.016) * 2.5); + el += (elT - el) * k; + place(); + renderer.render(scene, cam); + }, + destroy: function () { threeDispose(scene, [mat, geo, renderer]); }, + }; + }, + }); + + /* ringedWorld — a ringed gas giant with real single-scattering optics. Four + * systems on one scene graph: a 1024x512 DataTexture of domain-warped tiling + * noise banded by latitude (zonal belts, not a marble); a Rayleigh shell marched + * 16 view samples deep with a 5-step solar march and chromatic Beer-Lambert + * extinction, so the limb is blue and the terminator reddens because the light + * reaching it took the longest path through the air; an annulus with radial + * optical depth, ringlets and a Cassini-style gap; and — the point of the scene — + * MUTUAL SHADOWING computed analytically in both directions (the blocks marked + * SHADOW), the rings darkening the planet and the planet darkening the rings out + * of one shared ringTau(), so the shadow carries the gap structure with it. + * Everything renders into a half-float target, tonemapped once at the end so the + * additive atmosphere composites in linear light. + */ + registerThree("ringedWorld", { + defaults: { + colors: ["#f7e7c8", "#d59a5c", "#7a4f34", "#cfe0ff"], + background: "#03040a", + bands: 9.0, // zonal band count + ringInner: 1.34, + ringOuter: 2.44, + ringTau: 0.74, // vertical optical depth of the densest ringlets + spin: 0.055, // planet rotation, texture widths per second + sunElev: 15.5, // degrees above the ring plane + haze: 0.34, // overlying-haze extinction -> limb darkening + sun: 4.0, + exposure: 1.05, + distance: 4.55, + }, + + scene: function (T, h) { + var renderer = threeRenderer(T, h); + var o = h.opts; + var pal = paletteOf(o, ["#f7e7c8", "#d59a5c", "#7a4f34", "#cfe0ff"]); + function lin(c) { return new T.Vector3(Math.pow(c.r / 255, 2.2), Math.pow(c.g / 255, 2.2), Math.pow(c.b / 255, 2.2)); } + function P(i) { return lin(pal[i % pal.length]); } + var bg = lin(hexToRgb(o.background || "#03040a")); + + /* ---- CPU cloud deck: tiling value noise, banded by latitude ---------- */ + var TW = 1024, TH = 512, G = 256, GM = 255; + var lat0 = new Float32Array(G * G), sd = 20240719; + for (var i = 0; i < lat0.length; i++) { sd = (Math.imul(sd, 1664525) + 1013904223) >>> 0; lat0[i] = sd / 4294967296; } + function gv(x, y, m, s) { return lat0[((((x & m) + s) & GM) << 8) | ((y + s * 7) & GM)]; } + function vn(x, y, m, s) { // bilinear value noise, period (m+1) in x + var xi = Math.floor(x), yi = Math.floor(y), fx = x - xi, fy = y - yi; + fx = fx * fx * (3 - 2 * fx); fy = fy * fy * (3 - 2 * fy); + var a = gv(xi, yi, m, s) * (1 - fx) + gv(xi + 1, yi, m, s) * fx; + var b = gv(xi, yi + 1, m, s) * (1 - fx) + gv(xi + 1, yi + 1, m, s) * fx; + return a + (b - a) * fy; + } + // sy < 1 makes each octave vary fast in longitude and slowly in latitude, + // which is what stretches every eddy into a belt-parallel streak. `per` must + // be a power of two or the wrap in u grows a seam down the terminator. + function fbm(u, v, per, sy, s, oct) { + var amp = 0.5, t = 0, nrm = 0, p = per; + for (var k = 0; k < oct; k++) { t += amp * vn(u * p, v * p * sy, p - 1, s + k * 13); nrm += amp; amp *= 0.5; p *= 2; } + return t / nrm; + } + var data = new Uint8Array(TW * TH * 4), ptr = 0, nb = Math.max(3, o.bands); + var SU = [0.28, 0.72, 0.06], SV = [0.36, 0.60, 0.66], SR = [0.075, 0.045, 0.032]; + for (var j = 0; j < TH; j++) { + var v = (j + 0.5) / TH, phi = (v - 0.5) * Math.PI; + for (var ii = 0; ii < TW; ii++) { + var u = (ii + 0.5) / TW; + // shear the latitude coordinate: belt boundaries buckle and braid along + // their own length, the way a shear instability rolls up in a jet stream + var y = phi + (fbm(u, v, 8, 0.30, 1, 4) - 0.5) * 0.105 + (fbm(u, v, 32, 0.30, 2, 3) - 0.5) * 0.038; + var q = 0.5 + 0.5 * (0.60 * Math.sin(y * nb) + 0.30 * Math.sin(y * nb * 1.9 + 1.3) + 0.34 * Math.sin(y * nb * 0.44 - 0.6)); + q += (fbm(u, v, 32, 0.22, 3, 4) - 0.5) * 0.26; + var pole = Math.max(0, (Math.abs(phi) / 1.5708 - 0.62) / 0.38); + q = q * (1 - pole * pole) + 0.30 * pole * pole; // darker polar hoods + var st = 0; // anticyclonic ovals + for (var k2 = 0; k2 < 3; k2++) { + var du = u - SU[k2]; if (du > 0.5) du -= 1; if (du < -0.5) du += 1; + var dv = (v - SV[k2]) * 2.1, rr = Math.sqrt(du * du + dv * dv) / SR[k2]; + if (rr < 1.7) { // swirl the fine noise + var m = (1 - rr / 1.7) * (1 - rr / 1.7), ang = Math.atan2(dv, du) + rr * 1.9; + st = Math.max(st, m); + q += m * (0.30 + 0.34 * (vn(Math.cos(ang) * 9 + 40, Math.sin(ang) * 9 + rr * 5, 255, 7) - 0.5)); + } + } + q = q < 0 ? 0 : q > 1 ? 1 : q; + data[ptr++] = (q * 255) | 0; // R: band / cloud albedo + data[ptr++] = (fbm(u, v, 64, 0.18, 5, 3) * 255) | 0; // G: micro-contrast + data[ptr++] = (st * 255) | 0; data[ptr++] = 255; // B: storm mask + } + } + var map = new T.DataTexture(data, TW, TH, T.RGBAFormat); + map.wrapS = T.RepeatWrapping; map.wrapT = T.ClampToEdgeWrapping; + map.minFilter = T.LinearMipmapLinearFilter; map.magFilter = T.LinearFilter; + map.generateMipmaps = true; map.needsUpdate = true; + + /* ---- shared uniforms + the one density function both shadows use ----- */ + var uL = { value: new T.Vector3(1, 0, 0) }, uCam = { value: new T.Vector3() }; + var uRi = { value: o.ringInner }, uRo = { value: o.ringOuter }, uTauK = { value: o.ringTau }; + var uSpin = { value: 0 }, uSun = { value: o.sun }, uIce = { value: P(3) }; + var VERT = "out vec3 vP; void main(){ vP=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }"; + + // Radial optical depth of the sheet. The ring material AND the planet's + // shadow term both call this, so the shadow inherits every gap. + var RINGD = [ + "uniform float uRi, uRo, uTauK;", + "float h11(float x){ return fract(sin(x*127.1)*43758.5453); }", + "float vn1(float x){ float i=floor(x), f=fract(x); f=f*f*(3.0-2.0*f); return mix(h11(i),h11(i+1.0),f); }", + "float ringTau(float r){", + " if (r < uRi || r > uRo) return 0.0; float x = (r - uRi)/(uRo - uRi);", + // Band-limit the fine octaves to the screen footprint of one radial step. + // Both have a mean of 1, so fading them where they cannot be resolved kills + // the moire without shifting the sheet's average density. + " float fw = fwidth(x)*1.7;", + " float d = 0.58 + 0.42*sin(x*7.5 - 0.8);", // A/B plateaus + " d *= mix(1.0, 0.72 + 0.58*vn1(x*27.0 + 3.1), 1.0 - smoothstep(0.0, 0.075, fw));", // ringlets + " d *= mix(1.0, 0.66 + 0.68*vn1(x*104.0), 1.0 - smoothstep(0.0, 0.034, fw));", // grain + " d *= smoothstep(0.022, 0.070, abs(x - 0.47));", // Cassini gap + " float g = (x - 0.80)/0.013; d *= 1.0 - 0.85*exp(-g*g);", // Encke gap + " return max(d*smoothstep(0.0,0.05,x)*smoothstep(1.0,0.93,x), 0.0) * uTauK;", + "}"].join("\n"); + + var scene = new T.Scene(); + var cam = new T.PerspectiveCamera(38, h.width / Math.max(1, h.height), 0.05, 300); + + /* ---- planet --------------------------------------------------------- */ + var pu = { + uMap: { value: map }, uL: uL, uCam: uCam, uSpin: uSpin, uSun: uSun, + uRi: uRi, uRo: uRo, uTauK: uTauK, uIce: uIce, uHaze: { value: o.haze }, + uC0: { value: P(0) }, uC1: { value: P(1) }, uC2: { value: P(2) }, + // storm tint: the belt colour pushed brighter and redder, still palette-derived + uHot: { value: P(1).clone().multiply(new T.Vector3(1.75, 1.05, 0.72)) }, + }; + var planetMat = new T.ShaderMaterial({ + glslVersion: T.GLSL3, uniforms: pu, + vertexShader: "out vec2 vUv; out vec3 vP;\nvoid main(){ vUv=uv; vP=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }", + fragmentShader: ["precision highp float;", "uniform sampler2D uMap;", + "uniform vec3 uL,uCam,uC0,uC1,uC2,uHot,uIce; uniform float uSpin,uSun,uHaze;", + "in vec2 vUv; in vec3 vP; out vec4 outColor;", RINGD, + /* SHADOW #1 — THE RINGS ONTO THE PLANET. + Walk from the surface point toward the sun. The ring plane is y = 0, so + the crossing is at s = -p.y/L.y; if that lands inside the annulus the + sheet is between this point and the sun. The slant path through a sheet + of vertical depth tau is tau/sin(elevation) = tau/|L.y|, which is why a + low sun throws a much blacker ring shadow than a high one. */ + "float ringShadow(vec3 p){", + " if (abs(uL.y) < 1e-4) return 1.0; float s = -p.y/uL.y;", + " if (s <= 0.0) return 1.0;", // plane is anti-sunward + " return exp(-ringTau(length((p + uL*s).xz))/max(abs(uL.y), 0.05));", + "}", + "void main(){", + " vec3 n = normalize(vP); vec4 tx = texture(uMap, vec2(vUv.x + uSpin, vUv.y));", + " vec3 alb = mix(mix(uC2, uC1, smoothstep(0.0,0.54,tx.r)), uC0, smoothstep(0.46,1.0,tx.r));", + " alb = mix(alb, uHot, tx.b*0.80) * (0.84 + 0.30*tx.g); float nl = dot(n, uL);", + // wrapped diffuse: a deep scattering atmosphere carries light past the + // geometric terminator, so the day/night edge is soft, never a hard step + " float wrap = clamp((nl + 0.14)/1.14, 0.0, 1.0), li = wrap*wrap;", + // limb darkening as Beer-Lambert extinction of the overlying haze along + // the slant view path — 1/mu air masses, so the limb goes dark smoothly + " float mu = clamp(dot(n, normalize(uCam - vP)), 0.0, 1.0);", + " float ld = exp(-uHaze*(1.0/max(mu,0.09) - 1.0));", + " vec3 c = alb*(li*ld*ringShadow(vP)*uSun + 0.010);", + " outColor = vec4(c + alb*uIce*0.055*smoothstep(-0.55,0.25,-nl), 1.0);", // + ringshine + "}"].join("\n"), + }); + scene.add(new T.Mesh(new T.SphereGeometry(1, 128, 80), planetMat)); + + /* ---- Rayleigh shell -------------------------------------------------- */ + var RA = 1.105, au = { + uL: uL, uCam: uCam, uBeta: { value: new T.Vector3(2.60, 6.10, 15.5) }, + uSky: { value: P(3) }, uRa: { value: RA }, uHs: { value: 0.030 }, uSunI: { value: 5.6 }, + }; + var airMat = new T.ShaderMaterial({ + glslVersion: T.GLSL3, uniforms: au, transparent: true, blending: T.AdditiveBlending, + side: T.BackSide, depthTest: false, depthWrite: false, vertexShader: VERT, + fragmentShader: ["precision highp float;", + "uniform vec3 uL,uCam,uBeta,uSky; uniform float uRa,uHs,uSunI;", + "in vec3 vP; out vec4 outColor;", + "vec2 sph(vec3 ro, vec3 rd, float r){ float b=dot(ro,rd), c=dot(ro,ro)-r*r, d=b*b-c;", + " if (d < 0.0) return vec2(1.0,-1.0); d=sqrt(d); return vec2(-b-d,-b+d); }", + "float dns(vec3 p){ return exp(-(length(p)-1.0)/uHs); }", + // solar optical depth from a sample: 5 steps out to the shell, plus the + // planet's own shadow as a soft cylinder (a hard test bands the terminator) + "float odSun(vec3 p){", + " float sc = -dot(p, uL), od = 0.0, f = sph(p, uL, uRa).y/5.0;", + " float occ = sc > 0.0 ? smoothstep(0.985, 1.030, length(p + uL*sc)) : 1.0;", + " for (int i=0;i<5;i++) od += dns(p + uL*(f*(float(i)+0.5)));", + " return od*f + (1.0 - occ)*40.0;", + "}", + "void main(){", + " vec3 ro = uCam, rd = normalize(vP - uCam); vec2 a = sph(ro, rd, uRa);", + " if (a.y <= a.x) discard;", + " float t0 = max(a.x, 0.0), t1 = a.y; vec2 s = sph(ro, rd, 1.0);", + " if (s.y > 0.0 && s.x > t0) t1 = min(t1, s.x);", // stop at the cloud deck + " float mu = dot(rd, uL), ds = (t1-t0)/16.0, odV = 0.0;", + " vec3 acc = vec3(0.0), beta = uBeta*normalize(uSky+0.35)*1.7;", + " for (int i=0;i<16;i++){", + " vec3 p = ro + rd*(t0 + ds*(float(i)+0.5)); float d = dns(p)*ds; odV += d;", + // chromatic extinction over solar path + view path: blue is scrubbed + // first, so the long grazing paths leave the warm reddened terminator band + " acc += d*exp(-beta*(odSun(p) + odV));", + " }", + " outColor = vec4(acc*beta*(0.0596831*(1.0+mu*mu))*uSunI, 1.0);", // 3/16pi phase + "}"].join("\n"), + }); + var air = new T.Mesh(new T.SphereGeometry(RA, 72, 48), airMat); + air.renderOrder = 2; scene.add(air); + + /* ---- rings: a flat annulus built directly in the equatorial plane ---- */ + var SEG = 320, pos = [], idx = []; + for (var ri = 0; ri < 2; ri++) for (var si = 0; si <= SEG; si++) { + // distinct names: `ang`/`rr` are already var-declared in the cloud-texture + // block above, and var is function-scoped. + var rAng = si / SEG * Math.PI * 2, rRad = ri ? o.ringOuter : o.ringInner; + pos.push(Math.cos(rAng) * rRad, 0, Math.sin(rAng) * rRad); + } + for (var s2 = 0; s2 < SEG; s2++) idx.push(s2, SEG + 1 + s2, s2 + 1, s2 + 1, SEG + 1 + s2, SEG + 2 + s2); + var rg = new T.BufferGeometry(); + rg.setAttribute("position", new T.Float32BufferAttribute(pos, 3)); + rg.setIndex(idx); + var ru = { uL: uL, uCam: uCam, uRi: uRi, uRo: uRo, uTauK: uTauK, uIce: uIce, uSun: uSun, uWarm: { value: P(0) } }; + var ringMat = new T.ShaderMaterial({ + glslVersion: T.GLSL3, uniforms: ru, transparent: true, side: T.DoubleSide, + depthWrite: false, vertexShader: VERT, + fragmentShader: ["precision highp float;", + "uniform vec3 uL,uCam,uIce,uWarm; uniform float uSun;", + "in vec3 vP; out vec4 outColor;", RINGD, + "void main(){", + " float tau = ringTau(length(vP.xz)); if (tau <= 0.0005) discard;", + " vec3 rd = normalize(vP - uCam);", + /* SHADOW #2 — THE PLANET ONTO THE RINGS. + The planet's shadow is a cylinder of radius 1 pointing down -uL. The + closest approach of the ray vP + s*uL to the planet centre is at + s = -dot(vP,uL); if that is sunward and the perpendicular miss distance + is under one radius, this ring point sits in the umbra. The smoothstep + is the penumbra the star's finite angular size would cast. */ + " float sc = -dot(vP, uL); vec3 icy = mix(uIce, uWarm, 0.30);", + " float shp = sc > 0.0 ? smoothstep(0.985, 1.075, length(vP + uL*sc)) : 1.0;", + " float el = max(abs(uL.y), 0.02);", // sin of the solar elevation + " float alpha = 1.0 - exp(-tau/max(abs(rd.y), 0.42));", // slant depth to the eye + " float trans = exp(-tau/el);", // sunlight surviving the sheet + " float unlit = step(uL.y*(-rd.y), 0.0);", // eye and sun on opposite faces + // diffuse off the lit face, plus the forward/diffracted light that reaches + // the shaded face — bright gaps, dark plateaus, the Cassini negative look + " vec3 c = icy*shp*(0.10 + 0.85*el)*uSun*0.30*(1.0 - 0.78*unlit);", + " c += icy*shp*trans*unlit*uSun*0.30 + icy*0.010;", + " outColor = vec4(c, alpha);", + "}"].join("\n"), + }); + var rings = new T.Mesh(rg, ringMat); + rings.renderOrder = 3; scene.add(rings); + + /* ---- star field ----------------------------------------------------- */ + var su = { uBg: { value: bg }, uCool: { value: P(3) } }; + var skyMat = new T.ShaderMaterial({ + glslVersion: T.GLSL3, uniforms: su, side: T.BackSide, depthWrite: false, + depthTest: false, vertexShader: VERT, + fragmentShader: ["precision highp float;", "uniform vec3 uBg,uCool;", + "in vec3 vP; out vec4 outColor;", + "float h31(vec3 p){ return fract(sin(dot(p, vec3(12.9898,78.233,37.719)))*43758.5453); }", + "void main(){", + " vec3 d = normalize(vP), c = uBg*(0.75+0.5*(d.y*0.5+0.5)) + uCool*0.004;", + " for (int L=0;L<3;L++){", + " float sc = L==0 ? 34.0 : (L==1 ? 58.0 : 92.0);", + " vec3 g = d*sc, cel = floor(g), f = g - cel; float br = h31(cel + float(L)*4.1);", + " if (br > 0.70){", // one jittered point per cell, so never a grid + " vec3 of = clamp(vec3(h31(cel+11.7), h31(cel+23.3), h31(cel+31.9)), 0.12, 0.88);", + " float dd = length(f - of);", + " c += exp(-dd*dd*300.0)*(br-0.70)*3.4*mix(vec3(0.65,0.76,1.0), vec3(1.0,0.87,0.7), h31(cel+7.3));", + " } }", + " outColor = vec4(c, 1.0);", + "}"].join("\n"), + }); + var sky = new T.Mesh(new T.SphereGeometry(150, 32, 24), skyMat); + sky.renderOrder = -1; scene.add(sky); + + /* ---- half-float target, one tonemap at the end ---------------------- */ + var rt = new T.WebGLRenderTarget(Math.max(2, h.width * h.dpr), Math.max(2, h.height * h.dpr), { + type: T.HalfFloatType, minFilter: T.LinearFilter, magFilter: T.LinearFilter, depthBuffer: true, + }); + var qu = { uTex: { value: rt.texture }, uExp: { value: o.exposure } }; + var quadMat = new T.ShaderMaterial({ + glslVersion: T.GLSL3, uniforms: qu, depthTest: false, depthWrite: false, + vertexShader: "out vec2 vU; void main(){ vU=uv; gl_Position=vec4(position.xy,0.0,1.0); }", + fragmentShader: ["precision highp float;", "uniform sampler2D uTex; uniform float uExp;", + "in vec2 vU; out vec4 outColor;", + "void main(){", + " vec3 c = texture(uTex, vU).rgb * uExp * (1.0 - 0.30*dot(vU-0.5, vU-0.5));", + " c = (c*(2.30*c+0.05))/(c*(2.30*c+0.95)+0.16);", // filmic, then sRGB + " outColor = vec4(pow(max(c, 0.0), vec3(1.0/2.2)), 1.0);", + "}"].join("\n"), + }); + var quadScene = new T.Scene(); + quadScene.add(new T.Mesh(new T.PlaneGeometry(2, 2), quadMat)); + var quadCam = new T.OrthographicCamera(-1, 1, 1, -1, 0, 1); + + /* ---- animation ------------------------------------------------------ */ + var az = 0.9, el = -0.245, origin = new T.Vector3(); + function place(t) { + var my = h.mouse.active ? h.mouse.y / Math.max(1, h.height) : 0.52; + // The eye rides just BELOW the ring plane while the sun is above it, so we + // face the shadowed hemisphere and look at the rings' unlit side. + el += (-0.055 - (1 - my) * 0.40 - el) * 0.05; // mouse -> inclination + var A = az + t * 0.048, d = o.distance; // slow orbital drift + cam.position.set(Math.cos(A) * Math.cos(el) * d, Math.sin(el) * d, Math.sin(A) * Math.cos(el) * d); + cam.lookAt(origin); + uCam.value.copy(cam.position); + // The star holds a moderate phase angle to the eye so the lit hemisphere + // stays presented; librating both angles walks the ring shadow across the + // disk instead of parking it on the far side. + var se = o.sunElev * Math.PI / 180 + 0.085 * Math.sin(t * 0.085); + var sa = A + 0.86 + 0.22 * Math.sin(t * 0.062); + uL.value.set(Math.cos(sa) * Math.cos(se), Math.sin(se), Math.sin(sa) * Math.cos(se)).normalize(); + uSpin.value = t * o.spin; + } + place(0); + + return { + resize: function (w, hh) { + renderer.setPixelRatio(h.dpr); renderer.setSize(w, hh, false); + rt.setSize(Math.max(2, w * h.dpr), Math.max(2, hh * h.dpr)); + cam.aspect = w / Math.max(1, hh); cam.updateProjectionMatrix(); + }, + update: function (n) { + if (n.exposure != null) qu.uExp.value = n.exposure; + if (n.ringTau != null) uTauK.value = n.ringTau; + if (n.sun != null) uSun.value = n.sun; + if (n.haze != null) pu.uHaze.value = n.haze; + if (n.sunElev != null) o.sunElev = n.sunElev; + if (n.distance != null) o.distance = n.distance; + if (n.spin != null) o.spin = n.spin; + }, + draw: function (t) { + place(h.reduced ? 0 : t); + renderer.setRenderTarget(rt); renderer.render(scene, cam); + renderer.setRenderTarget(null); renderer.render(quadScene, quadCam); + }, + destroy: function () { + threeDispose(quadScene, []); + threeDispose(scene, [map, planetMat, airMat, ringMat, skyMat, quadMat, rt, renderer]); + }, + }; + }, + }); + + /* gravitySim — a real GPGPU gravitational integrator, not a fragment trick. + * Particle state (position + velocity) lives in floating-point render targets; + * once the initial conditions are seeded the CPU never touches a particle again. + * Two Plummer-softened masses ride a genuine *eccentric* Kepler orbit (Newton + * iteration on M = E - e sin E each step), so the pair plunges through pericentre + * once a period and raises fresh tidal bridges and tails; a diffuse Plummer halo + * at the barycentre keeps stripped material weakly bound; the pointer is a + * movable third mass. Integration is SYMPLECTIC — kick then drift (v += a(x)dt + * in one pass, then x += v dt in the next, reading the freshly written velocity). + * That ordering is the whole ballgame: explicit Euler with the same step pumps in + * energy every revolution and the disks unwind into numerical junk within + * seconds, while the symplectic map conserves a shadow Hamiltonian and the orbits + * stay closed for as long as the tab is open. dt is clamped so a stalled frame + * cannot blow the integrator up. Light accumulates in a half-float buffer and is + * tone-mapped in a resolve pass — that is what keeps a huge density range legible + * instead of clipping every core to a white blob. */ + var GS_QV = "varying vec2 vUv;\nvoid main(){ vUv = uv; gl_Position = vec4(position.xy, 0.0, 1.0); }"; + // Plummer softening: |a| = GM r / (r^2+eps^2)^1.5, bounded as r -> 0, so a + // particle threading a centre is slung out instead of reaching infinity. + var GS_FORCE = + "uniform sampler2D tPos;uniform sampler2D tVel;uniform vec4 uAtt[3];" + + "uniform float uEps2,uDt,uHalo,uHaloEps2;varying vec2 vUv;void main(){" + + "vec3 x=texture2D(tPos,vUv).xyz;vec3 v=texture2D(tVel,vUv).xyz;vec3 a=vec3(0.0);" + + "for(int i=0;i<3;i++){vec3 d=uAtt[i].xyz-x;float rr=dot(d,d)+uEps2;" + + "a+=d*(uAtt[i].w/(rr*sqrt(rr)));}float hr=dot(x,x)+uHaloEps2;" + + "a-=x*(uHalo/(hr*sqrt(hr)));gl_FragColor=vec4(v+a*uDt,1.0);}"; + var GS_DRIFT = + "uniform sampler2D tPos;\nuniform sampler2D tVel;\nuniform float uDt;\nvarying vec2 vUv;\n" + + "void main(){ gl_FragColor = vec4(texture2D(tPos, vUv).xyz + texture2D(tVel, vUv).xyz * uDt, 1.0); }"; + // Each vertex reads its own texel of the current position/velocity textures. + // Fast particles are the crowded ones, so shrinking them keeps cores from + // flattening into featureless discs and lets the faint outer streams read. + var GS_PTV = + "uniform sampler2D tPos;uniform sampler2D tVel;uniform float uSize,uVMax,uUseTex,uDpr;" + + "attribute vec2 aRef;attribute float aSpd;varying float vS;void main(){" + + "vec3 p=position;float sp=aSpd;" + + "if(uUseTex>0.5){p=texture2D(tPos,aRef).xyz;sp=length(texture2D(tVel,aRef).xyz);}" + + "vS=clamp(sp/uVMax,0.0,1.0);vec4 mv=modelViewMatrix*vec4(p,1.0);gl_Position=projectionMatrix*mv;" + + "gl_PointSize=clamp(uSize*uDpr*(190.0/max(1.0,-mv.z))*(1.2-0.35*vS),1.0,4.5);}"; + var GS_PTF = + "uniform vec3 uC0,uC1,uC2,uC3;uniform float uGlow,uEnc;varying float vS;void main(){" + + "vec2 q=gl_PointCoord-0.5;float d=dot(q,q);if(d>0.25)discard;" + + "float m=exp(-d*13.0)*(1.0-d*4.0);vec3 c=mix(uC0,uC1,smoothstep(0.0,0.34,vS));" + + "c=mix(c,uC2,smoothstep(0.3,0.68,vS));c=mix(c,uC3,smoothstep(0.62,1.0,vS));" + + "c*=(0.02+2.4*vS*vS*vS)*uGlow*m;" + + "if(uEnc>0.5)c=pow(max(c,0.0),vec3(0.4545));gl_FragColor=vec4(c,1.0);}"; + var GS_BG = + "uniform vec3 uBg,uHalo;uniform float uAsp,uEnc;varying vec2 vUv;void main(){" + + "vec2 p=vUv-0.5;p.x*=uAsp;float r=length(p);" + + "vec3 c=uBg+uHalo*(0.0030*exp(-r*r*2.2)+0.0022*exp(-r*4.5));" + + "if(uEnc>0.5)c=pow(max(c,0.0),vec3(0.4545));gl_FragColor=vec4(c,1.0);}"; + // Quarter-res 25-tap gather: the downsample does most of the blurring, so one + // pass buys a convincing core glow for almost nothing on software GL. + var GS_BLOOM = + "uniform sampler2D tSrc;uniform vec2 uTexel;varying vec2 vUv;void main(){" + + "vec3 s=vec3(0.0);float wsum=0.0;for(int y=-2;y<=2;y++)for(int x=-2;x<=2;x++){" + + "vec2 d=vec2(float(x),float(y));float w=exp(-dot(d,d)*0.32);" + + "s+=max(texture2D(tSrc,vUv+d*uTexel).rgb,0.0)*w;wsum+=w;}gl_FragColor=vec4(s/wsum,1.0);}"; + // Luminance-weighted Reinhard: compresses the range without bleaching a bright + // core to pure white the way per-channel clipping does, then encodes sRGB. + var GS_RESOLVE = + "uniform sampler2D tSrc;uniform sampler2D tBloom;uniform float uExp,uBloom;varying vec2 vUv;" + + "void main(){vec3 c=(max(texture2D(tSrc,vUv).rgb,0.0)+texture2D(tBloom,vUv).rgb*uBloom)*uExp;" + + "float L=dot(c,vec3(0.2126,0.7152,0.0722));c*=(L/(1.0+L))/max(L,1e-5);" + + "gl_FragColor=vec4(pow(min(c,vec3(1.0)),vec3(0.4545)),1.0);}"; + var GS_STARV = "varying vec3 vN;void main(){vN=normalize(normalMatrix*normal);" + + "gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}"; + var GS_STARF = "uniform vec3 uC;uniform float uEnc;varying vec3 vN;void main(){" + + "vec3 c=uC*(0.3+2.2*pow(max(0.0,vN.z),1.5));" + + "if(uEnc>0.5)c=pow(c,vec3(0.4545));gl_FragColor=vec4(c,1.0);}"; + + registerThree("gravitySim", { + defaults: { + grid: 160, speed: 1, mass: 9000, separation: 40, eccentricity: 0.45, + softening: 2.2, pointSize: 1.0, glow: 1, exposure: 1.6, bloom: 0.45, interactive: true, + colors: ["#2f4bff", "#22d3ee", "#ffd08a", "#fff3df"], background: "#03040a", + }, + scene: function (T, h) { + var renderer = threeRenderer(T, h); + var gl = renderer.getContext(); + // Float colour attachments are not universal. Probe before committing to the + // GPGPU path — without them every FBO reads back black and the scene dies. + var gpu = !!(gl.getExtension("EXT_color_buffer_float") || gl.getExtension("WEBGL_color_buffer_float")); + var o = h.opts, pal = paletteOf(o, ["#2f4bff", "#22d3ee", "#ffd08a", "#fff3df"]); + var N = gpu ? Math.max(32, Math.min(160, Math.round(o.grid || 160))) : 44, COUNT = N * N; + // sRGB hex -> linear light, or a #03040a background becomes a blue wash + function lin(i) { + var c = pal[Math.round((i / 3) * (pal.length - 1))]; + return new T.Vector3(Math.pow(c.r / 255, 2.2), Math.pow(c.g / 255, 2.2), Math.pow(c.b / 255, 2.2)); + } + var bc = hexToRgb(o.background || "#03040a"); + var bgv = new T.Vector3(Math.pow(bc.r / 255, 2.2), Math.pow(bc.g / 255, 2.2), Math.pow(bc.b / 255, 2.2)); + var MASS = o.mass || 9000, A = o.separation || 40, EPS = o.softening || 2.2; + var ECC = Math.max(0, Math.min(0.7, o.eccentricity === undefined ? 0.45 : o.eccentricity)); + var OM = Math.sqrt(2 * MASS / (A * A * A)); // Kepler mean motion + var HALO = 1.1 * MASS, HEPS = 30, VMAX = Math.sqrt(MASS / (EPS * 1.2)) + 10; + var RMIN = 1.5, RMAX = A * 0.30, TILT = 0.22, enc = gpu ? 0 : 1; + + // ---- the binary: true Kepler two-body solution, phased to start at apo ---- + var hp = new T.Vector3(), hv = new T.Vector3(), t2 = new T.Vector3(); + function hostAt(tt, out) { + var Ma = Math.PI + tt * OM, E = Ma, q; + for (q = 0; q < 5; q++) E -= (E - ECC * Math.sin(E) - Ma) / (1 - ECC * Math.cos(E)); + var r = A * (1 - ECC * Math.cos(E)) * 0.5; + var nu = Math.atan2(Math.sqrt(1 - ECC * ECC) * Math.sin(E), Math.cos(E) - ECC); + return out.set(Math.cos(nu) * r, 0, Math.sin(nu) * r); + } + hostAt(0, hp); hostAt(0.02, hv); hostAt(-0.02, t2); + hv.sub(t2).multiplyScalar(25); // central difference -> host velocity + + // ---- initial conditions: two inclined, near-circular co-moving disks ----- + var P = new Float32Array(COUNT * 4), V = new Float32Array(COUNT * 4); + var i, k, ct, st; + for (i = 0; i < COUNT; i++) { + var sg = (i % 2) === 0 ? 1 : -1; + var r = RMIN + (RMAX - RMIN) * Math.pow(Math.random(), 0.75); + var an = Math.random() * Math.PI * 2, zz = (Math.random() + Math.random() - 1) * 0.06 * r; + // exact circular speed for the softened potential, +-3% scatter + var vc = Math.sqrt(MASS * r * r / Math.pow(r * r + EPS * EPS, 1.5)) * (0.97 + Math.random() * 0.06); + var lx = r * Math.cos(an), lz = r * Math.sin(an); + var wx = -Math.sin(an) * vc, wz = Math.cos(an) * vc; + ct = Math.cos(TILT * sg); st = Math.sin(TILT * sg); k = i * 4; + P[k] = lx + sg * hp.x; P[k + 1] = zz * ct - lz * st; P[k + 2] = zz * st + lz * ct + sg * hp.z; + V[k] = wx + sg * hv.x; V[k + 1] = -wz * st; V[k + 2] = wz * ct + sg * hv.z; + } + + // ---- GPGPU plumbing ----------------------------------------------------- + var fsScene = new T.Scene(), fsCam = new T.OrthographicCamera(-1, 1, 1, -1, 0, 1); + var quadGeo = new T.PlaneGeometry(2, 2), quad = new T.Mesh(quadGeo); + quad.frustumCulled = false; fsScene.add(quad); + function mkrt(w, hh, ty) { + return new T.WebGLRenderTarget(w, hh, { minFilter: T.NearestFilter, magFilter: T.NearestFilter, + format: T.RGBAFormat, type: ty, depthBuffer: false, stencilBuffer: false, generateMipmaps: false }); + } + function pass(frag, u) { + return new T.ShaderMaterial({ vertexShader: GS_QV, fragmentShader: frag, + depthTest: false, depthWrite: false, uniforms: u }); + } + function dtex(a) { + var t = new T.DataTexture(a, N, N, T.RGBAFormat, T.FloatType); + t.minFilter = t.magFilter = T.NearestFilter; t.needsUpdate = true; return t; + } + function soft(t) { t.texture.minFilter = t.texture.magFilter = T.LinearFilter; return t; } + var rtP = null, rtV = null, texP = null, texV = null, forceMat = null, driftMat = null; + var rtHDR = null, rtBloom = null, resMat = null, bloomMat = null, curP = null, curV = null, wi = 0; + var att = [new T.Vector4(), new T.Vector4(), new T.Vector4(0, 0, 0, 0)]; + if (gpu) { + rtP = [mkrt(N, N, T.FloatType), mkrt(N, N, T.FloatType)]; + rtV = [mkrt(N, N, T.FloatType), mkrt(N, N, T.FloatType)]; + texP = dtex(P); texV = dtex(V); curP = texP; curV = texV; + rtHDR = soft(mkrt(2, 2, T.HalfFloatType)); rtBloom = soft(mkrt(2, 2, T.HalfFloatType)); + forceMat = pass(GS_FORCE, { tPos: { value: null }, tVel: { value: null }, uAtt: { value: att }, + uEps2: { value: EPS * EPS }, uDt: { value: 0 }, uHalo: { value: HALO }, uHaloEps2: { value: HEPS * HEPS } }); + driftMat = pass(GS_DRIFT, { tPos: { value: null }, tVel: { value: null }, uDt: { value: 0 } }); + bloomMat = pass(GS_BLOOM, { tSrc: { value: rtHDR.texture }, uTexel: { value: new T.Vector2(0.01, 0.01) } }); + resMat = pass(GS_RESOLVE, { tSrc: { value: rtHDR.texture }, tBloom: { value: rtBloom.texture }, + uExp: { value: o.exposure || 1.6 }, uBloom: { value: o.bloom === undefined ? 0.45 : o.bloom } }); + } + var bgMat = pass(GS_BG, { uBg: { value: bgv }, uAsp: { value: 1 }, + uEnc: { value: enc }, uHalo: { value: lin(1).lerp(lin(0), 0.55) } }); + + // ---- the visible scene graph ------------------------------------------- + var scene = new T.Scene(), geo = new T.BufferGeometry(); + var cam = new T.PerspectiveCamera(45, 1.7, 1, 900); + var ref = new Float32Array(COUNT * 2), spd = new Float32Array(COUNT), pos = new Float32Array(COUNT * 3); + for (i = 0; i < COUNT; i++) { + ref[i * 2] = ((i % N) + 0.5) / N; ref[i * 2 + 1] = (Math.floor(i / N) + 0.5) / N; + pos[i * 3] = P[i * 4]; pos[i * 3 + 1] = P[i * 4 + 1]; pos[i * 3 + 2] = P[i * 4 + 2]; + } + geo.setAttribute("position", new T.BufferAttribute(pos, 3)); + geo.setAttribute("aRef", new T.BufferAttribute(ref, 2)); + geo.setAttribute("aSpd", new T.BufferAttribute(spd, 1)); + var ptMat = new T.ShaderMaterial({ + vertexShader: GS_PTV, fragmentShader: GS_PTF, transparent: true, + blending: T.AdditiveBlending, depthTest: false, depthWrite: false, + uniforms: { tPos: { value: null }, tVel: { value: null }, uUseTex: { value: gpu ? 1 : 0 }, + uSize: { value: o.pointSize || 1 }, uVMax: { value: VMAX }, uDpr: { value: h.dpr }, + uGlow: { value: o.glow === undefined ? 1 : o.glow }, uEnc: { value: enc }, + uC0: { value: lin(0) }, uC1: { value: lin(1) }, uC2: { value: lin(2) }, uC3: { value: lin(3) } }, + }); + var pts = new T.Points(geo, ptMat); + pts.frustumCulled = false; scene.add(pts); + var starGeo = new T.SphereGeometry(0.8, 14, 10); + var starMat = new T.ShaderMaterial({ vertexShader: GS_STARV, fragmentShader: GS_STARF, + blending: T.AdditiveBlending, transparent: true, depthWrite: false, + uniforms: { uC: { value: lin(3) }, uEnc: { value: enc } } }); + var stars = [new T.Mesh(starGeo, starMat), new T.Mesh(starGeo, starMat)]; + scene.add(stars[0], stars[1]); + + // ---- stepping ----------------------------------------------------------- + var simT = 0, mm = 0, mp = new T.Vector3(), ray = new T.Vector3(); + function attractors() { + hostAt(simT, hp); att[0].set(hp.x, 0, hp.z, MASS); att[1].set(-hp.x, 0, -hp.z, MASS); + att[2].set(mp.x, mp.y, mp.z, mm * MASS * 0.85); + stars[0].position.set(hp.x, 0, hp.z); stars[1].position.set(-hp.x, 0, -hp.z); + } + function step(dt) { + simT += dt; attractors(); + if (!gpu) { cpuStep(dt); return; } + forceMat.uniforms.tPos.value = curP; forceMat.uniforms.tVel.value = curV; + forceMat.uniforms.uDt.value = dt; quad.material = forceMat; + renderer.setRenderTarget(rtV[wi]); renderer.render(fsScene, fsCam); + driftMat.uniforms.tPos.value = curP; driftMat.uniforms.tVel.value = rtV[wi].texture; + driftMat.uniforms.uDt.value = dt; quad.material = driftMat; + renderer.setRenderTarget(rtP[wi]); renderer.render(fsScene, fsCam); + curV = rtV[wi].texture; curP = rtP[wi].texture; wi = 1 - wi; + renderer.setRenderTarget(null); + } + function cpuStep(dt) { // identical symplectic map, fallback only + for (var j = 0; j < COUNT; j++) { + var q = j * 4, ax = 0, ay = 0, az = 0, m, rr, f, in3; + for (m = 0; m < 4; m++) { // 3 masses plus the halo at the origin + in3 = m < 3; + var dx = (in3 ? att[m].x : 0) - P[q], dy = (in3 ? att[m].y : 0) - P[q + 1]; + var dz = (in3 ? att[m].z : 0) - P[q + 2]; + rr = dx * dx + dy * dy + dz * dz + (in3 ? EPS * EPS : HEPS * HEPS); + f = (in3 ? att[m].w : HALO) / (rr * Math.sqrt(rr)); + ax += dx * f; ay += dy * f; az += dz * f; + } + V[q] += ax * dt; V[q + 1] += ay * dt; V[q + 2] += az * dt; + P[q] += V[q] * dt; P[q + 1] += V[q + 1] * dt; P[q + 2] += V[q + 2] * dt; + pos[j * 3] = P[q]; pos[j * 3 + 1] = P[q + 1]; pos[j * 3 + 2] = P[q + 2]; + spd[j] = Math.sqrt(V[q] * V[q] + V[q + 1] * V[q + 1] + V[q + 2] * V[q + 2]); + } + geo.attributes.position.needsUpdate = true; geo.attributes.aSpd.needsUpdate = true; + } + // Warm-up runs here, not in draw: reduced motion gets exactly one frame and + // it has to already show a post-pericentre bridge, not two pristine disks. + var warm = gpu ? 580 : 110; + for (i = 0; i < warm; i++) step(1 / 68); + + function aim(t) { + var a = 1.12 + t * 0.05; + cam.position.set(Math.sin(a) * 76, 33 + Math.sin(t * 0.09) * 6, Math.cos(a) * 76); + cam.lookAt(0, 0, 0); + } + aim(0); + return { + resize: function (w, hh) { + renderer.setPixelRatio(h.dpr); renderer.setSize(w, hh, false); + cam.aspect = w / Math.max(1, hh); cam.updateProjectionMatrix(); + bgMat.uniforms.uAsp.value = cam.aspect; ptMat.uniforms.uDpr.value = h.dpr; + if (rtHDR) { + var pw = Math.max(2, Math.round(w * h.dpr)), ph = Math.max(2, Math.round(hh * h.dpr)); + var bw = Math.max(2, pw >> 2), bh = Math.max(2, ph >> 2); + rtHDR.setSize(pw, ph); rtBloom.setSize(bw, bh); + bloomMat.uniforms.uTexel.value.set(1 / bw, 1 / bh); + } + }, + update: function (n) { + if (n.glow !== undefined) ptMat.uniforms.uGlow.value = n.glow; + if (n.pointSize !== undefined) ptMat.uniforms.uSize.value = n.pointSize; + if (resMat && n.exposure !== undefined) resMat.uniforms.uExp.value = n.exposure; + if (resMat && n.bloom !== undefined) resMat.uniforms.uBloom.value = n.bloom; + }, + draw: function (t, dt) { + var sdt = Math.min(dt || 1 / 60, 1 / 30) * (o.speed === undefined ? 1 : o.speed); + // the pointer becomes a third mass; ramping it keeps streams from snapping + var want = (o.interactive !== false && h.mouse && h.mouse.active) ? 1 : 0; + if (want) { + ray.set((h.mouse.x / Math.max(1, h.width)) * 2 - 1, 1 - (h.mouse.y / Math.max(1, h.height)) * 2, 0.5); + ray.unproject(cam).sub(cam.position).normalize(); + var kk = ray.y < -0.03 ? -cam.position.y / ray.y : 90; + mp.copy(cam.position).addScaledVector(ray, Math.max(6, Math.min(240, kk))); + } + mm += (want - mm) * 0.06; + if (!h.reduced) { step(sdt * 0.5); step(sdt * 0.5); } else attractors(); + aim(t); + ptMat.uniforms.tPos.value = curP; ptMat.uniforms.tVel.value = curV; + renderer.setRenderTarget(gpu ? rtHDR : null); + quad.material = bgMat; renderer.autoClear = true; renderer.render(fsScene, fsCam); + renderer.autoClear = false; renderer.render(scene, cam); renderer.autoClear = true; + if (gpu) { + quad.material = bloomMat; renderer.setRenderTarget(rtBloom); renderer.render(fsScene, fsCam); + renderer.setRenderTarget(null); + quad.material = resMat; renderer.render(fsScene, fsCam); + } + }, + destroy: function () { + var ex = [bgMat, ptMat, starMat, starGeo, quadGeo, renderer]; + if (gpu) ex = ex.concat([rtP[0], rtP[1], rtV[0], rtV[1], rtHDR, rtBloom, texP, texV, + forceMat, driftMat, bloomMat, resMat]); + threeDispose(scene, ex); + }, + }; + }, + }); + + /* starGlare — a hand-rolled HDR post chain (no EffectComposer, no addons). + * 13 render targets, 22 fullscreen passes + 1 scene render per frame: + * HDR scene -> bright-pass -> 4x (13-tap downsample + separable Gaussian) + * -> 4 tent upsamples, each added into the level below -> 2 anamorphic + * streak passes -> composite (ghosts, radial chromatic aberration, ACES). + * The pyramid matters: one blur radius gives you a tight core or a wide halo, + * never both, and reads as a cheap glow. Summing every mip is a lens' falloff. + */ + registerThree("starGlare", { + defaults: { + speed: 1, + bloomStrength: 1.0, + bloomRadius: 1.15, + threshold: 1.0, + streak: 0.7, + aberration: 1.0, + exposure: 0.9, + colors: ["#fff4d6", "#ffcf8a", "#a8c8ff", "#7d8dff"], + background: "#03040a", + }, + scene: function (T, h) { + var renderer = threeRenderer(T, h); + renderer.toneMapping = T.NoToneMapping; // we tonemap in the composite + renderer.setClearColor(0x000000, 1); + + var pal = paletteOf(h.opts, ["#fff4d6", "#ffcf8a", "#a8c8ff", "#7d8dff"]); + // Palette hex is sRGB; the whole chain is linear light, so linearise. + function lin(c) { return new T.Vector3(Math.pow(c.r / 255, 2.2), Math.pow(c.g / 255, 2.2), Math.pow(c.b / 255, 2.2)); } + var cStar = lin(pal[0]), cWarm = lin(pal[1 % pal.length]); + var cCool = lin(pal[2 % pal.length]), cDust = lin(pal[3 % pal.length]); + var bg = lin(hexToRgb(h.opts.background || "#03040a")); + + var scene = new T.Scene(); + var cam = new T.PerspectiveCamera(42, h.width / Math.max(1, h.height), 0.1, 400); + cam.position.set(0, 0, 15); + + /* ---- subject: a close binary behind a dark occluding body ---- */ + var VERT_Q = "varying vec2 vUv; void main(){ vUv=uv; gl_Position=vec4(position.xy,0.0,1.0); }"; + var GLOW = new T.ShaderMaterial({ + uniforms: { uC: { value: cStar }, uI: { value: 52.0 } }, + vertexShader: "varying vec2 vUv; void main(){ vUv=uv; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }", + fragmentShader: + "varying vec2 vUv; uniform vec3 uC; uniform float uI;" + + "void main(){ vec2 p=vUv*2.0-1.0; float r=length(p); if(r>1.0) discard;" + + " float k=max(0.0,1.0-r); float core=k*k*k*k*k*k*k*k;" + // pow() on a clamped base + " float halo=exp(-r*3.4)-exp(-3.4);" + + " gl_FragColor=vec4(uC*uI*(core+halo*0.055),1.0); }", + transparent: true, blending: T.AdditiveBlending, depthWrite: false, side: T.DoubleSide, + }); + var starGrp = new T.Group(); + scene.add(starGrp); + var glowA = new T.Mesh(new T.PlaneGeometry(3.6, 3.6), GLOW), coreA = new T.Mesh(new T.SphereGeometry(0.16, 20, 14), GLOW); + var glowB = new T.Mesh(new T.PlaneGeometry(1.7, 1.7), GLOW.clone()); + glowB.material.uniforms.uC.value = cWarm; glowB.material.uniforms.uI.value = 15.0; + var coreB = new T.Mesh(new T.SphereGeometry(0.07, 14, 10), glowB.material); + starGrp.add(glowA); starGrp.add(coreA); starGrp.add(glowB); starGrp.add(coreB); + + // Real PBR on the occluder, lit by a point light sitting inside the star, + // so its limb genuinely brightens as the star swings behind it. + var lightC = new T.Color(pal[0].r / 255, pal[0].g / 255, pal[0].b / 255); + var pl = new T.PointLight(lightC, 420, 0, 2); + scene.add(pl); + scene.add(new T.AmbientLight(0x14213a, 0.35)); + var occMat = new T.MeshStandardMaterial({ color: 0x0c1018, roughness: 0.92, metalness: 0.05 }); + var occ = new T.Mesh(new T.SphereGeometry(2.6, 96, 64), occMat); + occ.position.z = 3.4; + scene.add(occ); + // Back-facing shell: the opaque body draws first and hides all of it but + // the sliver outside its silhouette — an atmospheric limb. Density falls + // off with the ray's impact parameter, not with fresnel; fresnel peaks at + // the OUTER edge and gives a hard-edged grey donut instead of air. + var atmMat = new T.ShaderMaterial({ + uniforms: { + uC: { value: cCool }, uSun: { value: new T.Vector3(0, 0, -1) }, uI: { value: 2.6 }, + uEcl: { value: 0 }, uCen: { value: new T.Vector3() }, uRb: { value: 2.6 }, uRa: { value: 3.5 }, + }, + vertexShader: + "varying vec3 vR; varying vec3 vV;" + + "uniform vec3 uCen;" + + "void main(){ vec4 wp=modelMatrix*vec4(position,1.0); vR=wp.xyz-uCen;" + + " vV=normalize(cameraPosition-wp.xyz); gl_Position=projectionMatrix*viewMatrix*wp; }", + fragmentShader: + "varying vec3 vR; varying vec3 vV; uniform vec3 uC; uniform vec3 uSun;" + + "uniform float uI; uniform float uEcl; uniform float uRb; uniform float uRa;" + + "void main(){ vec3 V=-vV;" + + " vec3 pr=vR-V*dot(vR,V); float b=length(pr);" + // ray impact parameter + " float alt=clamp((b-uRb)/max(0.001,uRa-uRb),0.0,1.0);" + + " float dens=exp(-alt*4.5)*(1.0-alt);" + + // star direction projected into the screen plane: a raw dot(N,sun) + // has the sun's z lighting the entire ring evenly, which reads neon + " vec3 sd=uSun-V*dot(uSun,V); float ls=length(sd);" + + " float sn=(ls>1e-4&&b>1e-4)?max(0.0,dot(pr/b,sd/ls)):0.0;" + + " gl_FragColor=vec4(uC*uI*dens*(0.03+4.0*sn*sn*sn+0.9*uEcl),1.0); }", + transparent: true, blending: T.AdditiveBlending, depthWrite: false, side: T.BackSide, + }); + var atm = new T.Mesh(new T.SphereGeometry(3.5, 48, 32), atmMat); + scene.add(atm); + + /* ---- background stars + thin dust (Points, HDR colours) ---- */ + function cloud(n, spread, sz, col, gain) { + var pos = new Float32Array(n * 3), sc = new Float32Array(n), sd = new Float32Array(n), cl = new Float32Array(n * 3); + for (var i = 0; i < n; i++) { + // random point inside a shell — never floor(dir*k), that paints cubes + var u = Math.random() * 2 - 1, a = Math.random() * TAU(), rr = spread * (0.55 + 0.45 * Math.random()), s = Math.sqrt(Math.max(0, 1 - u * u)); + pos[i * 3] = Math.cos(a) * s * rr; pos[i * 3 + 1] = u * rr * 0.6; pos[i * 3 + 2] = Math.sin(a) * s * rr - spread * 0.35; + sc[i] = sz * (0.85 + Math.random() * Math.random() * 2.2); sd[i] = Math.random() * 10; + var mixc = Math.random() < 0.5 ? col : cCool, g = gain * (0.25 + Math.random() * Math.random() * 3.0); + cl[i * 3] = mixc.x * g; cl[i * 3 + 1] = mixc.y * g; cl[i * 3 + 2] = mixc.z * g; + } + var g2 = new T.BufferGeometry(); + g2.setAttribute("position", new T.BufferAttribute(pos, 3)); g2.setAttribute("aSize", new T.BufferAttribute(sc, 1)); + g2.setAttribute("aSeed", new T.BufferAttribute(sd, 1)); g2.setAttribute("aColor", new T.BufferAttribute(cl, 3)); + return g2; + } + var pMat = new T.ShaderMaterial({ + uniforms: { uScale: { value: 400 }, uTime: { value: 0 }, uSoft: { value: 3.4 } }, + vertexShader: + "attribute float aSize; attribute float aSeed; attribute vec3 aColor;" + + "uniform float uScale; uniform float uTime; varying vec3 vC;" + + "void main(){ float tw=0.72+0.28*sin(uTime*1.7+aSeed*6.283); vC=aColor*tw;" + + " vec4 mv=modelViewMatrix*vec4(position,1.0); gl_Position=projectionMatrix*mv;" + + " gl_PointSize=aSize*uScale/max(0.5,-mv.z); }", + fragmentShader: + "varying vec3 vC; uniform float uSoft;" + + "void main(){ vec2 p=gl_PointCoord*2.0-1.0; float r2=dot(p,p); if(r2>1.0) discard;" + + " gl_FragColor=vec4(vC*exp(-r2*uSoft),1.0); }", + transparent: true, blending: T.AdditiveBlending, depthWrite: false, + }); + var stars = new T.Points(cloud(950, 90, 0.5, cStar, 1.3), pMat); + scene.add(stars); + var dustMat = pMat.clone(); + dustMat.uniforms.uSoft.value = 3.4; + var dust = new T.Points(cloud(80, 24, 2.6, cDust, 0.03), dustMat); + scene.add(dust); + + /* ---------------- post chain ---------------- */ + var LV = 4; // downsample levels past the half-res bright pass + function mkRT(depth) { + return new T.WebGLRenderTarget(2, 2, { + type: T.HalfFloatType, minFilter: T.LinearFilter, magFilter: T.LinearFilter, + wrapS: T.ClampToEdgeWrapping, wrapT: T.ClampToEdgeWrapping, depthBuffer: !!depth, + }); + } + var rtScene = mkRT(true); // needs depth so the occluder actually occludes + var rtBright = mkRT(), rtBloom = mkRT(), rtSk1 = mkRT(), rtSk2 = mkRT(); + var down = [], up = [], i; + for (i = 0; i < LV; i++) { down.push(mkRT()); up.push(mkRT()); } + var allRT = [rtScene, rtBright, rtBloom, rtSk1, rtSk2].concat(down, up); + + function sm(frag, uni) { + return new T.ShaderMaterial({ uniforms: uni, vertexShader: VERT_Q, fragmentShader: frag, depthTest: false, depthWrite: false }); + } + var TAP = "vec3 tap(sampler2D s, vec2 uv, vec2 o){ return texture2D(s, uv+o).rgb; }\n"; + var mBright = sm( + "varying vec2 vUv; uniform sampler2D tS; uniform float uT;" + + "void main(){ vec3 c=texture2D(tS,vUv).rgb; float l=max(max(c.r,c.g),c.b);" + + " float kn=0.55; float s=clamp(l-uT+kn,0.0,2.0*kn); s=s*s/(4.0*kn);" + + " float w=max(s,l-uT)/max(l,1e-4); gl_FragColor=vec4(c*w,1.0); }", + { tS: { value: null }, uT: { value: 1.0 } }); + // 13-tap dual filter (the COD downsample), one pass per level. + var mDown = sm( + "varying vec2 vUv; uniform sampler2D tS; uniform vec2 uTx;\n" + TAP + + "void main(){ vec2 t=uTx;" + + " vec3 o=tap(tS,vUv,vec2(0.0))*0.125" + + " +(tap(tS,vUv,vec2(-2,2)*t)+tap(tS,vUv,vec2(2,2)*t)+tap(tS,vUv,vec2(-2,-2)*t)+tap(tS,vUv,vec2(2,-2)*t))*0.03125" + + " +(tap(tS,vUv,vec2(0,2)*t)+tap(tS,vUv,vec2(-2,0)*t)+tap(tS,vUv,vec2(2,0)*t)+tap(tS,vUv,vec2(0,-2)*t))*0.0625" + + " +(tap(tS,vUv,vec2(-1,1)*t)+tap(tS,vUv,vec2(1,1)*t)+tap(tS,vUv,vec2(-1,-1)*t)+tap(tS,vUv,vec2(1,-1)*t))*0.125;" + + " gl_FragColor=vec4(o,1.0); }", + { tS: { value: null }, uTx: { value: new T.Vector2() } }); + // Separable Gaussian on every mip. Without it the pyramid is a stack of + // box filters and a point source blooms into a visible SQUARE. + var mBlur = sm( + "varying vec2 vUv; uniform sampler2D tS; uniform vec2 uDir;\n" + TAP + + "void main(){ vec3 s=tap(tS,vUv,vec2(0.0))*0.227027;" + + " s+=(tap(tS,vUv,uDir*1.384615)+tap(tS,vUv,-uDir*1.384615))*0.316216;" + + " s+=(tap(tS,vUv,uDir*3.230769)+tap(tS,vUv,-uDir*3.230769))*0.070270;" + + " gl_FragColor=vec4(s,1.0); }", + { tS: { value: null }, uDir: { value: new T.Vector2() } }); + var mUp = sm( + "varying vec2 vUv; uniform sampler2D tS; uniform sampler2D tAdd; uniform vec2 uTx; uniform float uR;\n" + TAP + + "void main(){ vec2 t=uTx*uR;" + + " vec3 s=tap(tS,vUv,vec2(-1,1)*t)+tap(tS,vUv,vec2(0,1)*t)*2.0+tap(tS,vUv,vec2(1,1)*t)" + + " +tap(tS,vUv,vec2(-1,0)*t)*2.0+tap(tS,vUv,vec2(0.0))*4.0+tap(tS,vUv,vec2(1,0)*t)*2.0" + + " +tap(tS,vUv,vec2(-1,-1)*t)+tap(tS,vUv,vec2(0,-1)*t)*2.0+tap(tS,vUv,vec2(1,-1)*t);" + + " gl_FragColor=vec4(s*0.0625+texture2D(tAdd,vUv).rgb,1.0); }", + { tS: { value: null }, tAdd: { value: null }, uTx: { value: new T.Vector2() }, uR: { value: 1.15 } }); + // Anamorphic streak: long horizontal kernel, run twice at growing stride, + // tinted cool so it reads as coated glass rather than a smear. + var mStreak = sm( + "varying vec2 vUv; uniform sampler2D tS; uniform float uStep; uniform vec3 uTint; uniform float uAtt;" + + "void main(){ vec3 s=texture2D(tS,vUv).rgb; float wsum=1.0;" + + " for(int i=1;i<=14;i++){ float fi=float(i); float w=pow(uAtt,fi);" + + " s+=(texture2D(tS,vUv+vec2(uStep*fi,0.0)).rgb+texture2D(tS,vUv-vec2(uStep*fi,0.0)).rgb)*w; wsum+=2.0*w; }" + + " gl_FragColor=vec4(s/wsum*uTint,1.0); }", + { tS: { value: null }, uStep: { value: 0.002 }, uAtt: { value: 0.8 }, uTint: { value: new T.Vector3(1, 1, 1) } }); + var mComp = sm( + "varying vec2 vUv; uniform sampler2D tS; uniform sampler2D tB; uniform sampler2D tK; uniform sampler2D tG;" + + "uniform float uBloom; uniform float uStreak; uniform float uAb; uniform float uExp; uniform vec3 uBg;" + + // lens ghosts: an internal reflection is the image inverted through the + // optical axis, so each ghost is the bloom sampled at a negative scale + "vec3 ghost(vec2 d){ return texture2D(tG,0.5-d*0.45).rgb*vec3(0.09,0.05,0.03)" + + " + texture2D(tG,0.5-d*0.82).rgb*vec3(0.03,0.06,0.10)" + + " + texture2D(tG,0.5+d*1.55).rgb*vec3(0.05,0.03,0.08); }" + + "vec3 grab(vec2 uv){ vec2 d=uv-0.5; return texture2D(tS,uv).rgb + texture2D(tB,uv).rgb*uBloom" + + " + texture2D(tK,uv).rgb*uStreak + ghost(d)*uBloom; }" + + "vec3 aces(vec3 x){ return clamp((x*(2.51*x+0.03))/(x*(2.43*x+0.59)+0.14),0.0,1.0); }" + + "void main(){ vec2 d=vUv-0.5; float r2=dot(d,d);" + + " float ca=uAb*0.007*r2;" + // lateral CA: zero on axis, grows outward + " vec3 c; c.r=grab(0.5+d*(1.0-ca)).r; c.g=grab(vUv).g; c.b=grab(0.5+d*(1.0+ca)).b;" + + " c+=uBg*(1.2+16.0*exp(-2.4*r2));" + + " c=aces(c*uExp)*(1.0-0.55*r2);" + + " gl_FragColor=vec4(pow(max(c,vec3(0.0)),vec3(1.0/2.2)),1.0); }", + { tS: { value: null }, tB: { value: null }, tK: { value: null }, tG: { value: null }, uBg: { value: bg }, + uBloom: { value: 1.0 }, uStreak: { value: 0.7 }, uAb: { value: 1.0 }, uExp: { value: 0.9 } }); + + var quadGeo = new T.PlaneGeometry(2, 2), quad = new T.Mesh(quadGeo, mBright); + var qScene = new T.Scene(); qScene.add(quad); + var qCam = new T.OrthographicCamera(-1, 1, 1, -1, 0, 1); + function pass(mat, target) { + quad.material = mat; renderer.setRenderTarget(target || null); renderer.render(qScene, qCam); + } + // two-pass separable Gaussian, in place, using `tmp` as the H scratch + function gauss(rt, tmp) { + mBlur.uniforms.tS.value = rt.texture; mBlur.uniforms.uDir.value.set(1 / rt.width, 0); pass(mBlur, tmp); + mBlur.uniforms.tS.value = tmp.texture; mBlur.uniforms.uDir.value.set(0, 1 / rt.height); pass(mBlur, rt); + } + + function sizeAll(w, hh) { + var W = Math.max(2, Math.floor(w * h.dpr)), H = Math.max(2, Math.floor(hh * h.dpr)); + rtScene.setSize(W, H); + var bw = Math.max(2, W >> 1), bh = Math.max(2, H >> 1); + rtBright.setSize(bw, bh); rtBloom.setSize(bw, bh); rtSk1.setSize(bw, bh); rtSk2.setSize(bw, bh); + for (var k = 0; k < LV; k++) { + bw = Math.max(2, bw >> 1); bh = Math.max(2, bh >> 1); + down[k].setSize(bw, bh); up[k].setSize(bw, bh); + } + pMat.uniforms.uScale.value = H * 0.55; dustMat.uniforms.uScale.value = H * 0.55; + } + sizeAll(h.width, h.height); + + var opt = h.opts, ox = 0, oy = 0; + function readOpts() { + mBright.uniforms.uT.value = opt.threshold; mUp.uniforms.uR.value = opt.bloomRadius; + mComp.uniforms.uBloom.value = opt.bloomStrength * 0.30; mComp.uniforms.uStreak.value = opt.streak * 0.6; + mComp.uniforms.uAb.value = opt.aberration; mComp.uniforms.uExp.value = opt.exposure; + mStreak.uniforms.uTint.value.set(0.70, 0.83, 1.22); + } + readOpts(); + + return { + resize: function (w, hh) { + renderer.setPixelRatio(h.dpr); renderer.setSize(w, hh, false); + cam.aspect = w / Math.max(1, hh); cam.updateProjectionMatrix(); sizeAll(w, hh); + }, + update: function (o) { opt = o; readOpts(); }, + draw: function (t) { + var s = t * (opt.speed || 1) * 0.6; + // one base frequency + integer harmonics => the whole thing loops + var bx = 4.3 * Math.sin(s), by = 1.5 * Math.cos(2 * s) + 0.62; + if (h.mouse && h.mouse.active) { // drag the body across the star yourself + var mx = (h.mouse.x / Math.max(1, h.width)) * 2 - 1, my = -((h.mouse.y / Math.max(1, h.height)) * 2 - 1); + ox += (mx * 4.6 - bx - ox) * 0.12; oy += (my * 2.8 - by - oy) * 0.12; + } else { ox += (0 - ox) * 0.05; oy += (0 - oy) * 0.05; } + occ.position.x = bx + ox; occ.position.y = by + oy; + occ.rotation.y = s * 0.35; occ.rotation.x = 0.2; + atm.position.copy(occ.position); atmMat.uniforms.uCen.value.copy(occ.position); + atmMat.uniforms.uSun.value.copy(occ.position).multiplyScalar(-1).normalize(); + // a dead-centre occultation lights the whole limb: ring of fire + var lat = Math.sqrt(occ.position.x * occ.position.x + occ.position.y * occ.position.y) / 2.6; + var ecl = clamp(1 - lat, 0, 1); atmMat.uniforms.uEcl.value = ecl * ecl * 1.4; + // binary: the companion swings round the primary at 3x the base rate + var ang = s * 3.0; + glowB.position.set(Math.cos(ang) * 0.62, Math.sin(ang) * 0.2, Math.sin(ang) * 0.62); + coreB.position.copy(glowB.position); + glowA.quaternion.copy(cam.quaternion); glowB.quaternion.copy(cam.quaternion); + pMat.uniforms.uTime.value = t; dustMat.uniforms.uTime.value = t; + dust.rotation.z = s * 0.06; stars.rotation.y = s * 0.012; + cam.position.set(Math.sin(s * 0.5) * 1.15, Math.sin(s) * 0.5 + 0.25, 15); cam.lookAt(0, 0, 0); + renderer.setRenderTarget(rtScene); renderer.render(scene, cam); + + mBright.uniforms.tS.value = rtScene.texture; pass(mBright, rtBright); + // streaks come off the *unblurred* bright pass; blur first and the + // flare fattens into a bar + mStreak.uniforms.tS.value = rtBright.texture; mStreak.uniforms.uAtt.value = 0.90; + mStreak.uniforms.uStep.value = 1.5 / rtBright.width; pass(mStreak, rtSk1); + mStreak.uniforms.tS.value = rtSk1.texture; mStreak.uniforms.uAtt.value = 0.80; + mStreak.uniforms.uStep.value = 5.0 / rtSk1.width; pass(mStreak, rtSk2); + // level 0 blur; rtSk1 is free again now the streak has been captured + gauss(rtBright, rtSk1); + var src = rtBright, k; + for (k = 0; k < LV; k++) { + mDown.uniforms.tS.value = src.texture; mDown.uniforms.uTx.value.set(1 / src.width, 1 / src.height); + pass(mDown, down[k]); + gauss(down[k], up[k]); src = down[k]; // up[k] is free until upsampling + } + // upsample, adding each coarse level into the finer one below it + var cur = down[LV - 1]; + for (k = LV - 2; k >= 0; k--) { + mUp.uniforms.tS.value = cur.texture; mUp.uniforms.tAdd.value = down[k].texture; + mUp.uniforms.uTx.value.set(1 / cur.width, 1 / cur.height); pass(mUp, up[k]); cur = up[k]; + } + mUp.uniforms.tS.value = cur.texture; mUp.uniforms.tAdd.value = rtBright.texture; + mUp.uniforms.uTx.value.set(1 / cur.width, 1 / cur.height); pass(mUp, rtBloom); + + mComp.uniforms.tS.value = rtScene.texture; mComp.uniforms.tB.value = rtBloom.texture; + mComp.uniforms.tK.value = rtSk2.texture; mComp.uniforms.tG.value = down[1].texture; + pass(mComp, null); + }, + destroy: function () { + threeDispose(scene, [quadGeo, mBright, mDown, mBlur, mUp, atmMat, mStreak, mComp, pMat, dustMat, GLOW, glowB.material, occMat].concat(allRT, [renderer])); + }, + }; + }, + }); + /* ============================================================ * UI Components * ========================================================== */ @@ -6628,6 +8497,10 @@ register: function (name, def) { registerAnimation(name, def); return Galaxy; }, list: function () { return Object.keys(animations); }, defaults: function (name) { return animations[name] ? Object.assign({}, animations[name].defaults) : null; }, + /* Which tier draws this one: "2d" | "webgl2" | "three". */ + rendererOf: function (name) { return animations[name] ? animations[name].renderer : null; }, + /* Hand in your own three.js so the optional scenes never touch the network. */ + useThree: function (T) { threeMod = T; return Galaxy; }, autoInit: autoInit, // components toast: toast, diff --git a/galaxy.manifest.json b/galaxy.manifest.json index 06ab719..39261c9 100644 --- a/galaxy.manifest.json +++ b/galaxy.manifest.json @@ -1,8 +1,8 @@ { "name": "galaxyjs", - "version": "3.3.0", + "version": "3.4.0", "title": "GalaxyJS", - "description": "Zero-dependency cosmic animation & UI component library with one unified API.", + "description": "Cosmic animation & UI component library with one unified API. Zero required dependencies; six optional three.js scenes.", "homepage": "https://github.com/Edwson/GalaxyJS", "author": { "name": "Ed Chen", @@ -21,6 +21,8 @@ "create": "Galaxy.create(type, target, options) -> instance{ start, stop, pause, resume, update, destroy }", "scrollScene": "Galaxy.scrollScene(stickyStage, { scenes:[type|{type,options}], track?, onProgress?, reducedScene? }) -> { progress, layers, destroy } — bind scroll progress to a crossfading cinematic sequence of scenes; runs only the visible pair (battery-friendly), reduced-motion shows one static frame", "register": "Galaxy.register(name, { defaults, setup })", + "rendererOf": "Galaxy.rendererOf(name) -> \"2d\" | \"webgl2\" | \"three\"", + "useThree": "Galaxy.useThree(THREE) — supply your own three.js so the optional scenes never touch the network", "theme": "Galaxy.theme(\"dark\"|\"light\"|{ accent, bg, ... })", "toggleTheme": "Galaxy.toggleTheme()", "autoInit": "Galaxy.autoInit(scope?) — runs automatically; also wires data-* components", @@ -60,10 +62,29 @@ "gx-radio-c", "gx-range" ], + "tiers": { + "2d": 72, + "webgl2": 8, + "three": 6 + }, + "optionalDependency": { + "name": "three", + "version": "0.185.1", + "usedBy": [ + "eventHorizon", + "molecularCloud", + "spiralForge", + "ringedWorld", + "gravitySim", + "starGlare" + ], + "how": "Loaded on demand by dynamic import the first time one of these scenes mounts; nothing is requested otherwise. Pass { threeUrl } to point elsewhere, or call Galaxy.useThree(THREE) to skip the network. If it cannot load, the scene paints a still poster instead of an empty box." + }, "animations": [ { "name": "starfield", "desc": "Drifting parallax stars with twinkle", + "renderer": "2d", "options": { "count": 160, "speed": 1, @@ -79,6 +100,7 @@ { "name": "warp", "desc": "Hyperspace streaks from the center", + "renderer": "2d", "options": { "count": 220, "speed": 1, @@ -94,6 +116,7 @@ { "name": "blackHole", "desc": "Accretion disk with gravitational-lensing glow", + "renderer": "2d", "options": { "radius": 0.18, "speed": 1, @@ -109,6 +132,7 @@ { "name": "nebula", "desc": "Drifting layered gas clouds", + "renderer": "2d", "options": { "count": 7, "speed": 1, @@ -125,6 +149,7 @@ { "name": "spiral", "desc": "Rotating logarithmic galaxy arms", + "renderer": "2d", "options": { "stars": 600, "arms": 3, @@ -141,6 +166,7 @@ { "name": "meteors", "desc": "Diagonal shooting stars", + "renderer": "2d", "options": { "count": 18, "speed": 1, @@ -156,6 +182,7 @@ { "name": "constellation", "desc": "Connected node network, reacts to the pointer", + "renderer": "2d", "options": { "count": 90, "speed": 1, @@ -171,6 +198,7 @@ { "name": "particles", "desc": "Cursor repel / attract particle field", + "renderer": "2d", "options": { "count": 120, "speed": 1, @@ -187,6 +215,7 @@ { "name": "aurora", "desc": "Flowing ribbons of light", + "renderer": "2d", "options": { "bands": 4, "speed": 1, @@ -202,6 +231,7 @@ { "name": "wormhole", "desc": "Receding tunnel of rings", + "renderer": "2d", "options": { "rings": 26, "speed": 1, @@ -216,6 +246,7 @@ { "name": "orbits", "desc": "Planets circling a star", + "renderer": "2d", "options": { "bodies": 6, "speed": 1, @@ -232,6 +263,7 @@ { "name": "pulsar", "desc": "Rhythmic expanding rings", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -245,6 +277,7 @@ { "name": "gradient", "desc": "Animated mesh-gradient backdrop", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -258,6 +291,7 @@ { "name": "fireflies", "desc": "Wandering glow that breathes (interactive)", + "renderer": "2d", "options": { "count": 60, "speed": 1, @@ -273,6 +307,7 @@ { "name": "matrix", "desc": "Falling digital rain", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -286,6 +321,7 @@ { "name": "plasma", "desc": "Smooth flowing color field", + "renderer": "2d", "options": { "speed": 1, "scale": 1, @@ -299,6 +335,7 @@ { "name": "fireworks", "desc": "Launching shells that burst", + "renderer": "2d", "options": { "rate": 1, "speed": 1, @@ -316,6 +353,7 @@ { "name": "snow", "desc": "Drifting snowfall with wind", + "renderer": "2d", "options": { "count": 200, "speed": 1, @@ -330,6 +368,7 @@ { "name": "waves", "desc": "Layered ocean of sine waves", + "renderer": "2d", "options": { "layers": 4, "speed": 1, @@ -346,6 +385,7 @@ { "name": "dna", "desc": "Rotating double helix", + "renderer": "2d", "options": { "speed": 1, "points": 36, @@ -360,6 +400,7 @@ { "name": "lightning", "desc": "Branching bolts that flash", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -373,6 +414,7 @@ { "name": "ripples", "desc": "Expanding concentric rings (interactive)", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -386,6 +428,7 @@ { "name": "comets", "desc": "Glowing comets with trailing tails", + "renderer": "2d", "options": { "count": 4, "speed": 1, @@ -402,6 +445,7 @@ { "name": "confetti", "desc": "Celebratory falling paper", + "renderer": "2d", "options": { "rate": 1, "speed": 1, @@ -419,6 +463,7 @@ { "name": "bubbles", "desc": "Gentle rising bubbles", + "renderer": "2d", "options": { "count": 42, "speed": 1, @@ -433,6 +478,7 @@ { "name": "fog", "desc": "Drifting layered mist", + "renderer": "2d", "options": { "layers": 7, "speed": 1, @@ -447,6 +493,7 @@ { "name": "grid", "desc": "Synthwave perspective floor", + "renderer": "2d", "options": { "speed": 1, "lines": 16, @@ -460,6 +507,7 @@ { "name": "rain", "desc": "Angled rainfall", + "renderer": "2d", "options": { "count": 300, "speed": 1, @@ -474,6 +522,7 @@ { "name": "vortex", "desc": "Particles spiralling into the core", + "renderer": "2d", "options": { "count": 240, "speed": 1, @@ -488,6 +537,7 @@ { "name": "sparkle", "desc": "Twinkling glints", + "renderer": "2d", "options": { "count": 54, "speed": 1, @@ -502,6 +552,7 @@ { "name": "tunnel", "desc": "Neon polygon tunnel", + "renderer": "2d", "options": { "rings": 20, "sides": 6, @@ -517,6 +568,7 @@ { "name": "swarm", "desc": "Flocking boids, reacts to the pointer", + "renderer": "2d", "options": { "count": 64, "speed": 1, @@ -531,6 +583,7 @@ { "name": "ribbons", "desc": "Flowing silk ribbons", + "renderer": "2d", "options": { "count": 5, "speed": 1, @@ -545,6 +598,7 @@ { "name": "flowfield", "desc": "Particles following a flow field", + "renderer": "2d", "options": { "count": 600, "speed": 1, @@ -560,6 +614,7 @@ { "name": "globe", "desc": "Rotating dotted sphere", + "renderer": "2d", "options": { "count": 320, "speed": 1, @@ -573,6 +628,7 @@ { "name": "heartbeat", "desc": "ECG monitor sweep", + "renderer": "2d", "options": { "speed": 1, "bpm": 1, @@ -585,6 +641,7 @@ { "name": "equalizer", "desc": "Audio frequency bars", + "renderer": "2d", "options": { "bars": 32, "speed": 1, @@ -599,6 +656,7 @@ { "name": "clock", "desc": "Luminous analog clock (real time)", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -612,6 +670,7 @@ { "name": "rays", "desc": "Rotating volumetric light rays", + "renderer": "2d", "options": { "count": 14, "speed": 1, @@ -626,6 +685,7 @@ { "name": "radar", "desc": "Sweeping radar with blips", + "renderer": "2d", "options": { "speed": 1, "blips": 7, @@ -639,6 +699,7 @@ { "name": "embers", "desc": "Rising fire sparks", + "renderer": "2d", "options": { "count": 120, "speed": 1, @@ -653,6 +714,7 @@ { "name": "typewriter", "desc": "Typing text effect (set `text`)", + "renderer": "2d", "options": { "speed": 1, "text": [ @@ -671,6 +733,7 @@ { "name": "spirograph", "desc": "Evolving hypotrochoid curves", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -684,6 +747,7 @@ { "name": "supernova", "desc": "A star charges, collapses, then erupts in a shockwave", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -699,6 +763,7 @@ { "name": "quasar", "desc": "Black-hole core with edge-on accretion disk + twin jets", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -713,6 +778,7 @@ { "name": "starcluster", "desc": "A dense globular cluster rotating in 3D with depth", + "renderer": "2d", "options": { "speed": 1, "count": 280, @@ -728,6 +794,7 @@ { "name": "cosmicWeb", "desc": "Large-scale filaments linking glowing nodes", + "renderer": "2d", "options": { "speed": 1, "nodes": 34, @@ -743,6 +810,7 @@ { "name": "eclipse", "desc": "Dark disk ringed by a living corona + diamond-ring flash", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -757,6 +825,7 @@ { "name": "corona", "desc": "A roiling star with granulation and arcing prominences", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -772,6 +841,7 @@ { "name": "galaxyMerge", "desc": "Two galactic cores orbiting with tidal star streams", + "renderer": "2d", "options": { "speed": 1, "stars": 480, @@ -787,6 +857,7 @@ { "name": "lattice", "desc": "A rotating crystalline icosahedron, glowing edges", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -800,6 +871,7 @@ { "name": "moire", "desc": "Two drifting ring sets interfering into shimmer", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -813,6 +885,7 @@ { "name": "starburst", "desc": "A breathing lens flare with chromatic spikes", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -827,6 +900,7 @@ { "name": "pillars", "desc": "Towering nebular gas columns with embedded glints", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -842,6 +916,7 @@ { "name": "ionstorm", "desc": "Electric plasma arcs leaping from a core to the rim", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -856,6 +931,7 @@ { "name": "stardust", "desc": "Ultra-fine luminous dust on a parallax current", + "renderer": "2d", "options": { "speed": 1, "count": 320, @@ -872,6 +948,7 @@ { "name": "orrery", "desc": "Tilted concentric rings turning like an armillary", + "renderer": "2d", "options": { "speed": 1, "rings": 5, @@ -887,6 +964,7 @@ { "name": "oscilloscope", "desc": "Morphing Lissajous curves on a phosphor screen", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -899,6 +977,7 @@ { "name": "bokeh", "desc": "Defocused additive light discs drifting through depth", + "renderer": "2d", "options": { "speed": 1, "count": 26, @@ -914,6 +993,7 @@ { "name": "magnetosphere", "desc": "Dipole field lines with pole-to-pole particle streams", + "renderer": "2d", "options": { "speed": 1, "colors": [ @@ -928,6 +1008,7 @@ { "name": "lensing", "desc": "Schwarzschild lensing of a background starfield, with the Einstein ring", + "renderer": "webgl2", "options": { "mass": 1, "spin": 0.35, @@ -944,6 +1025,7 @@ { "name": "accretionDisk", "desc": "Accretion disk with relativistic Doppler beaming and gravitational redshift", + "renderer": "webgl2", "options": { "inclination": 1.15, "beta": 0.5, @@ -960,6 +1042,7 @@ { "name": "nBody", "desc": "Gravitational n-body integration with real orbital trails", + "renderer": "2d", "options": { "count": 13, "gravity": 1, @@ -978,6 +1061,7 @@ { "name": "tidalStream", "desc": "A satellite torn into leading and trailing tidal tails past the Roche limit", + "renderer": "2d", "options": { "count": 620, "gravity": 1, @@ -995,6 +1079,7 @@ { "name": "inspiral", "desc": "Binary inspiral shedding orbital energy as gravitational waves", + "renderer": "2d", "options": { "speed": 1, "decay": 1, @@ -1009,6 +1094,7 @@ { "name": "volumetricNebula", "desc": "Raymarched gas with Beer-Lambert absorption and Henyey-Greenstein scattering", + "renderer": "webgl2", "options": { "speed": 1, "density": 1, @@ -1026,6 +1112,7 @@ { "name": "starSurface", "desc": "A photosphere with convective granulation, limb darkening, spots and prominences", + "renderer": "webgl2", "options": { "speed": 1, "temperature": 5200, @@ -1042,6 +1129,7 @@ { "name": "atmosphere", "desc": "A planetary limb with Rayleigh and Mie scattering through a decaying shell", + "renderer": "webgl2", "options": { "speed": 1, "radius": 1, @@ -1059,6 +1147,7 @@ { "name": "dustLanes", "desc": "Interstellar dust with self-shadowing, silhouetted against a luminous field", + "renderer": "webgl2", "options": { "speed": 1, "density": 1, @@ -1075,6 +1164,7 @@ { "name": "protoplanetary", "desc": "A flared young-star disk with planet-carved gaps and scattering asymmetry", + "renderer": "webgl2", "options": { "speed": 1, "inclination": 62, @@ -1092,6 +1182,7 @@ { "name": "spectrograph", "desc": "A stellar absorption spectrum with named lines, scannable by pointer", + "renderer": "2d", "options": { "temperature": 5778, "lines": true, @@ -1107,6 +1198,7 @@ { "name": "transitCurve", "desc": "An exoplanet transit light curve with limb-darkened ingress and egress", + "renderer": "2d", "options": { "radiusRatio": 0.12, "period": 3.4, @@ -1124,6 +1216,7 @@ { "name": "waterfall", "desc": "A radio-telescope spectrogram scrolling in time", + "renderer": "2d", "options": { "bins": 96, "noise": 1, @@ -1139,6 +1232,7 @@ { "name": "hrDiagram", "desc": "A Hertzsprung-Russell diagram with a synthetic stellar population", + "renderer": "2d", "options": { "count": 150, "evolve": 1, @@ -1154,6 +1248,7 @@ { "name": "pulsarTiming", "desc": "A folded pulse profile building up, with timing residuals below", + "renderer": "2d", "options": { "period": 5.757451924, "jitter": 1.2, @@ -1169,6 +1264,7 @@ { "name": "gravityWell", "desc": "Drag a mass and watch particles fall into real inverse-square orbits", + "renderer": "2d", "options": { "count": 700, "mass": 1, @@ -1190,6 +1286,7 @@ { "name": "nebulaPaint", "desc": "Paint a nebula into an advecting, diffusing fluid field", + "renderer": "2d", "options": { "resolution": 96, "dissipation": 0.9955, @@ -1209,6 +1306,7 @@ { "name": "solarWind", "desc": "Solar wind deflected around a magnetosphere, with bow shock and polar cusps", + "renderer": "2d", "options": { "count": 520, "pressure": 1, @@ -1227,6 +1325,7 @@ { "name": "starForge", "desc": "Seed star systems that condense out of collapsing clouds", + "renderer": "2d", "options": { "maxSystems": 4, "formation": 1, @@ -1244,6 +1343,7 @@ { "name": "relativisticJets", "desc": "Twin relativistic jets from a spinning black hole and its torus", + "renderer": "webgl2", "options": { "beta": 0.96, "tilt": 0.75, @@ -1257,6 +1357,140 @@ "background": "#03040d", "interactive": true } + }, + { + "name": "eventHorizon", + "desc": "Schwarzschild black hole ray-traced in curved spacetime, with Doppler beaming", + "renderer": "three", + "options": { + "spin": 1, + "tilt": 0.42, + "diskInner": 2.6, + "diskOuter": 9, + "steps": 220, + "exposure": 0.85, + "colors": [ + "#fff4e2", + "#ffb46b", + "#7c5cff" + ], + "background": "#03040a", + "interactive": true + } + }, + { + "name": "molecularCloud", + "desc": "Raymarched 3D volume lit from within by an embedded protostar", + "renderer": "three", + "options": { + "colors": [ + "#ffb37a", + "#78aaff", + "#c58cff" + ], + "background": "#04060e", + "res": 128, + "steps": 64, + "density": 9, + "shadow": 2.6, + "emission": 1.35, + "forward": 0.62, + "spin": 0.13, + "churn": 0.3, + "distance": 2.02 + } + }, + { + "name": "spiralForge", + "desc": "340,000 GPU-instanced stars on differential-rotation orbits", + "renderer": "three", + "options": { + "stars": 340000, + "arms": 2, + "pitch": 0.5, + "spin": 1, + "bulge": 0.26, + "thickness": 1, + "tilt": 36, + "size": 1, + "tint": 0.34, + "colors": [ + "#ffc98a", + "#fff4e6", + "#a8c8ff" + ], + "background": "#03040c" + } + }, + { + "name": "ringedWorld", + "desc": "Gas giant with Rayleigh atmosphere and mutual ring/planet shadowing", + "renderer": "three", + "options": { + "colors": [ + "#f7e7c8", + "#d59a5c", + "#7a4f34", + "#cfe0ff" + ], + "background": "#03040a", + "bands": 9, + "ringInner": 1.34, + "ringOuter": 2.44, + "ringTau": 0.74, + "spin": 0.055, + "sunElev": 15.5, + "haze": 0.34, + "sun": 4, + "exposure": 1.05, + "distance": 4.55 + } + }, + { + "name": "gravitySim", + "desc": "GPGPU leapfrog N-body: two interacting disks with tidal tails", + "renderer": "three", + "options": { + "grid": 160, + "speed": 1, + "mass": 9000, + "separation": 40, + "eccentricity": 0.45, + "softening": 2.2, + "pointSize": 1, + "glow": 1, + "exposure": 1.6, + "bloom": 0.45, + "interactive": true, + "colors": [ + "#2f4bff", + "#22d3ee", + "#ffd08a", + "#fff3df" + ], + "background": "#03040a" + } + }, + { + "name": "starGlare", + "desc": "Hand-rolled HDR bloom, anamorphic streak and lens aberration chain", + "renderer": "three", + "options": { + "speed": 1, + "bloomStrength": 1, + "bloomRadius": 1.15, + "threshold": 1, + "streak": 0.7, + "aberration": 1, + "exposure": 0.9, + "colors": [ + "#fff4d6", + "#ffcf8a", + "#a8c8ff", + "#7d8dff" + ], + "background": "#03040a" + } } ], "components": [ diff --git a/galaxy.min.js b/galaxy.min.js index de5d1e3..c1cad4a 100644 --- a/galaxy.min.js +++ b/galaxy.min.js @@ -1 +1 @@ -!function(t,e){"object"==typeof module&&module.exports?module.exports=e():"function"==typeof define&&define.amd?define([],e):t.Galaxy=e()}("undefined"!=typeof self?self:this,function(){"use strict";var t="undefined"!=typeof document,e=t&&"function"==typeof matchMedia&&matchMedia("(prefers-reduced-motion: reduce)").matches;function a(t){return t?"string"==typeof t?document.querySelector(t):t:null}function o(t,e){return t+Math.random()*(e-t)}function r(t,e,a){return t+(e-t)*a}function i(t,e,a){return Math.max(e,Math.min(a,t))}function n(){return 2*Math.PI}function l(t){if("string"!=typeof t)return{r:124,g:92,b:255};3===(t=t.replace("#","").trim()).length&&(t=t.split("").map(function(t){return t+t}).join(""));var e=parseInt(t,16);return isNaN(e)?{r:124,g:92,b:255}:{r:e>>16&255,g:e>>8&255,b:255&e}}function s(t,e){return"rgba("+t.r+","+t.g+","+t.b+","+e+")"}function h(t,e,a){try{"function"==typeof CustomEvent&&t.dispatchEvent(new CustomEvent(e,{detail:a}))}catch(t){}}function c(t,e,a){return{r:Math.round(r(t.r,e.r,a)),g:Math.round(r(t.g,e.g,a)),b:Math.round(r(t.b,e.b,a))}}function f(t,e){return(t.colors&&t.colors.length?t.colors:e).map(l)}var d=["float","vec2","vec3","vec4"];function p(t){return"number"==typeof t?1:t.length}function u(t,e,a){var o=t.createShader(e);return o?(t.shaderSource(o,a),t.compileShader(o),t.getShaderParameter(o,t.COMPILE_STATUS)?o:(t.isContextLost()||"undefined"==typeof console||console.error("GalaxyJS: shader compile failed\n"+t.getShaderInfoLog(o)),t.deleteShader(o),null)):null}function v(t,e,a){null!==e&&("number"==typeof a?t.uniform1f(e,a):2===a.length?t.uniform2f(e,a[0],a[1]):3===a.length?t.uniform3f(e,a[0],a[1],a[2]):t.uniform4f(e,a[0],a[1],a[2],a[3]))}function g(t){return{draw:function(){var e=t.ctx,a=t.width,o=t.height,r=f(t.opts,["#7c5cff","#22d3ee"]);e.fillStyle=t.opts.background||"#05060f",e.fillRect(0,0,a,o);var i=e.createRadialGradient(.5*a,.5*o,0,.5*a,.5*o,.7*Math.max(a,o));i.addColorStop(0,s(r[0],.55)),i.addColorStop(1,s(r[r.length-1],0)),e.fillStyle=i,e.fillRect(0,0,a,o)}}}function x(t,e){y(t,{renderer:"webgl2",defaults:e.defaults||{},fallback:e.fallback||null,setup:function(t){var a=t.gl,o=e.uniforms?e.uniforms(t.opts,t):{},r=function(t,e,a){var o="";for(var r in a)Object.prototype.hasOwnProperty.call(a,r)&&(o+="uniform "+d[p(a[r])-1]+" "+r+";\n");var i="#version 300 es\nprecision highp float;\nuniform vec2 uResolution;\nuniform float uTime;\nuniform vec3 uMouse;\n"+o+"out vec4 fragColor;\n"+e,n=u(t,t.VERTEX_SHADER,"#version 300 es\nvoid main(){vec2 p=vec2(float((gl_VertexID<<1)&2),float(gl_VertexID&2));gl_Position=vec4(p*2.0-1.0,0.0,1.0);}"),l=u(t,t.FRAGMENT_SHADER,i);if(!n||!l)return null;var s=t.createProgram();return t.attachShader(s,n),t.attachShader(s,l),t.linkProgram(s),t.deleteShader(n),t.deleteShader(l),t.getProgramParameter(s,t.LINK_STATUS)?s:(t.isContextLost()||"undefined"==typeof console||console.error("GalaxyJS: shader link failed\n"+t.getProgramInfoLog(s)),t.deleteProgram(s),null)}(a,e.fragment,o);if(!r)return g(t);var i={};function n(t){return t in i||(i[t]=a.getUniformLocation(r,t)),i[t]}return a.useProgram(r),{draw:function(o){if(!a.isContextLost()){a.useProgram(r),a.uniform2f(n("uResolution"),t.canvas.width,t.canvas.height),a.uniform1f(n("uTime"),void 0!==e.staticTime&&t.reduced?e.staticTime:o),a.uniform3f(n("uMouse"),t.mouse.x*t.dpr,(t.height-t.mouse.y)*t.dpr,t.mouse.active?1:0);var i=e.uniforms?e.uniforms(t.opts,t):{};for(var l in i)Object.prototype.hasOwnProperty.call(i,l)&&v(a,n(l),i[l]);a.drawArrays(a.TRIANGLES,0,3)}},destroy:function(){r&&!a.isContextLost()&&a.deleteProgram(r)}}}})}var m=function(){var e=[],a=null;function o(t){for(var r=0;r=0&&e.splice(o,1),e.length||null===a||(cancelAnimationFrame(a),a=null)}}}(),b={};function y(t,e){b[t]={setup:e.setup,defaults:e.defaults||{},renderer:e.renderer||"2d",fallback:e.fallback||null}}function M(o,r,i){var n=a(r);if(!n)throw new Error('GalaxyJS: target not found for "'+o+'"');var l=b[o];if(!l)throw new Error('GalaxyJS: unknown animation "'+o+'"');n.classList.add("gx-surface-host");var s=document.createElement("canvas");s.className="gx-canvas",n.appendChild(s);var h=Object.assign({},l.defaults,i||{}),c=null,f=null,d=l.setup;if("webgl2"===l.renderer){try{c=s.getContext("webgl2",{alpha:!0,antialias:!1,premultipliedAlpha:!0,powerPreference:"low-power"})}catch(t){c=null}c||(d=l.fallback||g)}c||(f=s.getContext("2d"));var p={el:n,canvas:s,ctx:f,gl:c,opts:h,width:1,height:1,dpr:1,mouse:{x:-9999,y:-9999,active:!1},reduced:e,t:0},u=d(p);function v(){var t=n.getBoundingClientRect();p.width=Math.max(1,Math.round(t.width)),p.height=Math.max(1,Math.round(t.height)),p.dpr=Math.min(window.devicePixelRatio||1,2),s.width=Math.round(p.width*p.dpr),s.height=Math.round(p.height*p.dpr),s.style.width=p.width+"px",s.style.height=p.height+"px",c?c.viewport(0,0,s.width,s.height):f.setTransform(p.dpr,0,0,p.dpr,0,0),u.resize&&u.resize(p.width,p.height)}function x(t){var e=n.getBoundingClientRect(),a=t.touches?t.touches[0]:t;p.mouse.x=a.clientX-e.left,p.mouse.y=a.clientY-e.top,p.mouse.active=!0}function y(){p.mouse.active=!1,p.mouse.x=-9999,p.mouse.y=-9999}h.interactive&&(s.setAttribute("data-interactive","true"),n.addEventListener("pointermove",x),n.addEventListener("pointerleave",y)),v();var M="undefined"!=typeof performance?performance.now():Date.now(),w={running:!1,fn:function(t){var e=Math.min(.05,(t-M)/1e3);M=t,p.t+=e,u.draw(p.t,e)}},S=null,k=null,C=!1;function T(){return e?(u.draw(0,0),R):(w.running||(w.running=!0,M="undefined"!=typeof performance?performance.now():Date.now(),m.add(w)),R)}function P(){return w.running=!1,m.remove(w),R}"undefined"!=typeof ResizeObserver?(S=new ResizeObserver(v)).observe(n):t&&window.addEventListener("resize",v),"undefined"==typeof IntersectionObserver||e||(k=new IntersectionObserver(function(t){var e=t[0].isIntersecting;e&&!C?T():(C=!1,e||P())},{threshold:.01})).observe(n);var R={el:n,canvas:s,type:o,start:T,stop:P,pause:function(){return P(),C=!0,R},resume:function(){return C=!1,T(),R},update:function(t){return Object.assign(p.opts,t||{}),u.update?u.update(p.opts):u.resize&&u.resize(p.width,p.height),e&&u.draw(p.t,0),R},options:function(){return p.opts},destroy:function(){P(),S&&S.disconnect(),k&&k.disconnect(),h.interactive&&(n.removeEventListener("pointermove",x),n.removeEventListener("pointerleave",y)),u.destroy&&u.destroy(),s.parentNode&&s.parentNode.removeChild(s),n.classList.remove("gx-surface-host")}};return!1!==h.autoplay&&T(),R}function w(t,e){var a=t.ctx;!1===t.opts.trail||e<=0?a.clearRect(0,0,t.width,t.height):(a.fillStyle=s(l(t.opts.background||"#05060f"),e),a.fillRect(0,0,t.width,t.height))}function S(t){var e=t.ctx;e.clearRect(0,0,t.width,t.height),e.fillStyle=s(l(t.opts.background||"#05060f"),1),e.fillRect(0,0,t.width,t.height)}function k(t){var e=l(t);return[e.r/255,e.g/255,e.b/255]}function C(t,e,a){var o=f(t,e),r=o[Math.min(a,o.length-1)];return[r.r/255,r.g/255,r.b/255]}function T(t,e){var a=i(e,0,1)*(t.length-1),o=Math.min(t.length-2,Math.floor(a));return c(t[o],t[o+1],a-o)}function P(t){var e=l(t);return[e.r/255,e.g/255,e.b/255]}function R(t,e,a){var o=t&&t.colors&&t.colors.length?t.colors:null;return P(o&&o[e]||a)}function z(t,e,a,o){return i("number"==typeof t&&isFinite(t)?t:e,a,o)}function L(t,e){var a,o,r,n=i(t,1200,2e4)/100;n<=66?(a=255,o=99.4708025861*Math.log(n)-161.1195681661,r=n<=19?0:138.5177312231*Math.log(n-10)-305.0447927307):(a=329.698727446*Math.pow(n-60,-.1332047592),o=288.1221695283*Math.pow(n-60,-.0755148492),r=255);var l=void 0===e?1:e;return[i(a,0,255)/255*l,i(o,0,255)/255*l,i(r,0,255)/255*l]}y("starfield",{defaults:{count:160,speed:1,colors:["#ffffff","#bcd4ff","#fff1c4"],background:"#05060f",trail:!1},setup:function(t){var e=[];function a(){e=[];for(var a=t.width*t.height,r=Math.max(40,Math.round(t.opts.count*a/921600)),i=f(t.opts,["#ffffff"]),l=0;lt.height+2&&(l.y=-2,l.x=Math.random()*t.width);var h=.5+.5*Math.sin(a*(1+l.z)+l.tw);r.beginPath(),r.fillStyle=s(l.col,.25+.6*h),r.arc(l.x,l.y,l.r*(.6+l.z),0,n()),r.fill()}}}}}),y("warp",{defaults:{count:220,speed:1,colors:["#ffffff","#9bd0ff","#c9b8ff"],background:"#03040d",trail:!0},setup:function(t){var e,a=[],l=0,h=0;function c(){return{a:o(0,n()),d:o(0,1),len:0,col:e[Math.random()*e.length|0]}}function d(){e=f(t.opts,["#ffffff"]),l=t.width/2,h=t.height/2,a=[];for(var o=0;o1&&(u.d=o(0,.15),u.a=o(0,n()),v=u.d);var g=v*d,x=u.d*d,m=l+Math.cos(u.a)*g,b=h+Math.sin(u.a)*g,y=l+Math.cos(u.a)*x,M=h+Math.sin(u.a)*x;f.strokeStyle=s(u.col,i(u.d,.1,1)),f.lineWidth=r(.4,2.4,u.d),f.beginPath(),f.moveTo(m,b),f.lineTo(y,M),f.stroke()}}}}}),y("blackHole",{defaults:{radius:.18,speed:1,colors:["#ff7b00","#ffd166","#7c5cff"],background:"#04040c",particles:220},setup:function(t){var e,a,r,i,l=[];function h(){i=f(t.opts,["#ff7b00","#ffd166"]),e=t.width/2,a=t.height/2,r=Math.min(t.width,t.height)*t.opts.radius,l=[];for(var s=0;s1.2)&&(c.vx*=-1),(c.y<-.2||c.y>1.2)&&(c.vy*=-1);var f=c.x*t.width,d=c.y*t.height,p=c.r*i*(.9+.1*Math.sin(.6*e+h)),u=r.createRadialGradient(f,d,0,f,d,p);u.addColorStop(0,s(c.col,.5)),u.addColorStop(.5,s(c.col,.16)),u.addColorStop(1,"rgba(0,0,0,0)"),r.fillStyle=u,r.beginPath(),r.arc(f,d,p,0,n()),r.fill()}r.globalCompositeOperation="source-over"}}}}),y("spiral",{defaults:{stars:600,arms:3,speed:1,colors:["#ffffff","#9bd0ff","#c9b8ff","#ffd6a5"],background:"#04040c"},setup:function(t){var e,a,r,i=[];function l(){r=f(t.opts,["#ffffff","#9bd0ff"]),e=t.width/2,a=t.height/2,i=[];for(var l=.46*Math.min(t.width,t.height),s=0;st.width+50||u.y>t.height+50)a[p]=i();else{var g=u.x-f*u.len,x=u.y-d*u.len,m=l.createLinearGradient(u.x,u.y,g,x);m.addColorStop(0,s(u.col,.9)),m.addColorStop(1,"rgba(0,0,0,0)"),l.strokeStyle=m,l.lineWidth=2,l.lineCap="round",l.beginPath(),l.moveTo(u.x,u.y),l.lineTo(g,x),l.stroke()}}}}}}),y("constellation",{defaults:{count:90,speed:1,link:130,colors:["#7c5cff","#22d3ee"],background:"#05060f",interactive:!0},setup:function(t){var e,a=[];function r(){e=f(t.opts,["#7c5cff","#22d3ee"]);var r=t.width*t.height,i=Math.max(24,Math.round(t.opts.count*r/921600));a=[];for(var n=0;nt.width)&&(f.vx*=-1),(f.y<0||f.y>t.height)&&(f.vy*=-1),t.mouse.active){var d=f.x-t.mouse.x,p=f.y-t.mouse.y,u=Math.hypot(d,p);u<140&&u>.1&&(f.x+=d/u*.8,f.y+=p/u*.8)}}for(var v=0;v.1){var u=(120-p)/120*l*1.4;c.vx+=f/p*u,c.vy+=d/p*u}}c.vx*=.96,c.vy*=.96,c.x+=(c.vx+o(-.05,.05))*t.opts.speed,c.y+=(c.vy+o(-.05,.05))*t.opts.speed,c.x<0&&(c.x=t.width),c.x>t.width&&(c.x=0),c.y<0&&(c.y=t.height),c.y>t.height&&(c.y=0),i.fillStyle=s(c.col,.85),i.beginPath(),i.arc(c.x,c.y,c.r,0,n()),i.fill()}}}}}),y("aurora",{defaults:{bands:4,speed:1,colors:["#22d3ee","#7c5cff","#34d399","#f472b6"],background:"#04060f"},setup:function(t){var e;function a(){e=f(t.opts,["#22d3ee","#7c5cff"])}return a(),{resize:a,draw:function(a,o){var r=t.ctx;r.clearRect(0,0,t.width,t.height),r.fillStyle=s(l(t.opts.background),1),r.fillRect(0,0,t.width,t.height),r.globalCompositeOperation="lighter";for(var i=0;i.1&&(h.x+=c/d*1.2,h.y+=f/d*1.2)}h.x<0&&(h.x=t.width),h.x>t.width&&(h.x=0),h.y<0&&(h.y=t.height),h.y>t.height&&(h.y=0);var p=.25+.75*(.5+.5*Math.sin(2.2*a+h.ph)),u=5*h.r,v=i.createRadialGradient(h.x,h.y,0,h.x,h.y,u);v.addColorStop(0,s(h.col,.9*p)),v.addColorStop(1,s(h.col,0)),i.fillStyle=v,i.beginPath(),i.arc(h.x,h.y,u,0,n()),i.fill(),i.fillStyle=s(h.col,p),i.beginPath(),i.arc(h.x,h.y,.5*h.r,0,n()),i.fill()}i.globalCompositeOperation="source-over"}}}}),y("matrix",{defaults:{speed:1,colors:["#34d399"],background:"#020509",font:16,glyphs:"01"},setup:function(t){var e,a,r,i,n;function h(){n=l(t.opts.colors&&t.opts.colors[0]||"#34d399"),a=t.opts.font,i=t.opts.glyphs||"01アカサタナハマヤラ0123456789",e=Math.ceil(t.width/a),r=[];for(var s=0;st.height&&Math.random()>.975&&(p.y=o(-200,0))}}}}}),y("plasma",{defaults:{speed:1,scale:1,colors:["#7c5cff","#22d3ee","#f472b6"]},setup:function(t){var e,a;function o(){e=f(t.opts,["#7c5cff","#22d3ee","#f472b6"]),a=Math.max(10,22/(t.opts.scale||1))}function r(t){var a=e.length,o=(t%1+1)%1,r=Math.floor(o*a),i=(r+1)%a;return c(e[r],e[i],o*a-r)}return o(),{resize:o,draw:function(e,o){for(var i=t.ctx,n=e*t.opts.speed,l=0;l=0;d--){var p=e[d];p.y+=p.vy*t.opts.speed,p.vy+=.06,f.fillStyle=s(p.col,1),f.beginPath(),f.arc(p.x,p.y,2,0,n()),f.fill(),(p.y<=p.target||p.vy>=0)&&(h(p.x,p.y,p.col),e.splice(d,1))}for(var u=a.length-1;u>=0;u--){var v=a[u];v.vx*=.985,v.vy=.985*v.vy+t.opts.gravity,v.x+=v.vx*t.opts.speed,v.y+=v.vy*t.opts.speed,v.life-=.55*c,v.life<=0?a.splice(u,1):(f.fillStyle=s(v.col,v.life),f.beginPath(),f.arc(v.x,v.y,1.8*v.life+.4,0,n()),f.fill())}f.globalCompositeOperation="source-over"}}}}),y("snow",{defaults:{count:200,speed:1,wind:.4,colors:["#ffffff","#dbeafe"],background:"#0a0f1f"},setup:function(t){var e,a;function r(){a=f(t.opts,["#ffffff"]);var r=Math.max(40,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var i=0;it.height+4&&(l.y=-4,l.x=Math.random()*t.width),l.x>t.width+4&&(l.x=-4),l.x<-4&&(l.x=t.width+4),r.fillStyle=s(l.col,.85),r.beginPath(),r.arc(l.x,l.y,l.r,0,n()),r.fill()}}}}}),y("waves",{defaults:{layers:4,speed:1,amplitude:26,colors:["#0ea5e9","#22d3ee","#7c5cff","#1e3a8a"],background:"#040814"},setup:function(t){var e;function a(){e=f(t.opts,["#0ea5e9","#22d3ee"])}return a(),{resize:a,draw:function(a,o){S(t);for(var r=t.ctx,i=0;ir+o(60,160))break}}(o(.2*t.width,.8*t.width),0,!1),a=h,r=1,i=o(.4,1.6)/t.opts.speed),a)if((r-=2.4*l)<=0)a=null;else{var f=Math.min(.12,.12*r);c.fillStyle=s(e[0],f),c.fillRect(0,0,t.width,t.height),c.globalCompositeOperation="lighter",c.lineCap="round";for(var d=0;d=0;f--){var d=e[f];d.r+=70*t.opts.speed*i;var p=1-d.r/d.max;p<=0?e.splice(f,1):(c.strokeStyle=s(d.col,.8*p),c.lineWidth=2*p+.4,c.beginPath(),c.arc(d.x,d.y,d.r,0,n()),c.stroke())}}}}}),y("comets",{defaults:{count:4,speed:1,colors:["#ffffff","#a5c8ff","#c9b8ff","#ffd6a5"],background:"#04050d",stars:!0},setup:function(t){var e,a,r;function i(){var e=Math.random()<.5,a=o(1.2,2.4);return{x:e?-40:t.width+40,y:o(0,.7*t.height),vx:(e?1:-1)*a,vy:o(.2,.8)*a,hist:[],size:o(2.5,5),col:r[Math.random()*r.length|0]}}function l(){r=f(t.opts,["#ffffff","#a5c8ff"]),e=[];for(var n=0;n26&&f.hist.shift(),f.x<-80||f.x>t.width+80||f.y>t.height+80)e[c]=i();else{for(var d=0;dn&&e.length<460;)e.push(l()),r-=n;for(var h=e.length-1;h>=0;h--){var c=e[h];c.vy+=t.opts.gravity*o*6,c.y+=c.vy*t.opts.speed,c.x+=(c.vx+.7*Math.sin(2*a+c.sway))*t.opts.speed,c.rot+=c.vr,c.y>t.height+20?e.splice(h,1):(i.save(),i.translate(c.x,c.y),i.rotate(c.rot),i.fillStyle=s(c.col,1),i.fillRect(-c.w/2,-c.hh/2,c.w,c.hh*(.55+.45*Math.abs(Math.cos(c.rot)))),i.restore())}}}}}),y("bubbles",{defaults:{count:42,speed:1,colors:["#a8e6ff","#7c5cff","#34d399"],background:"#04121e"},setup:function(t){var e,a;function r(e){return{x:o(0,t.width),y:e?o(0,t.height):t.height+20,r:o(5,22),sp:o(.3,1),wob:o(0,n()),col:a[Math.random()*a.length|0]}}function i(){a=f(t.opts,["#a8e6ff"]);var o=Math.max(14,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var i=0;i1.3&&(h.x=-.3),h.x<-.3&&(h.x=1.3);var c=h.x*t.width,f=(h.y+.03*Math.sin(.3*a+l))*t.height,d=h.r*i*.6,p=r.createRadialGradient(c,f,0,c,f,d);p.addColorStop(0,s(h.col,.16)),p.addColorStop(1,s(h.col,0)),r.fillStyle=p,r.beginPath(),r.arc(c,f,d,0,n()),r.fill()}}}}}),y("grid",{defaults:{speed:1,lines:16,colors:["#f472b6","#7c5cff"],background:"#0a0114"},setup:function(t){var e,a,o;function r(){e=f(t.opts,["#f472b6","#7c5cff"]),a=t.width/2,o=.42*t.height}return r(),{resize:r,draw:function(r,i){S(t);var n=t.ctx,l=n.createRadialGradient(a,o,0,a,o,.4*t.height);l.addColorStop(0,s(e[0],.35)),l.addColorStop(1,s(e[0],0)),n.fillStyle=l,n.fillRect(0,0,t.width,o+20),n.shadowColor=s(e[1]||e[0],.8),n.shadowBlur=8,n.strokeStyle=s(e[1]||e[0],.8),n.lineWidth=1.4;for(var h=t.opts.lines,c=r*t.opts.speed*.35%1,f=0;ft.height+10&&(d.y=-10,d.x=o(-.1,1.1)*t.width),n.strokeStyle=s(d.col,.5),n.beginPath(),n.moveTo(d.x,d.y),n.lineTo(d.x-h*d.len,d.y-c*d.len),n.stroke()}}}}}),y("vortex",{defaults:{count:240,speed:1,colors:["#7c5cff","#22d3ee","#f472b6"],background:"#04040c"},setup:function(t){var e,a,r,i,l;function h(t){return{a:o(0,n()),r:t?Math.random():1,sp:o(.4,1),col:l[Math.random()*l.length|0]}}function c(){l=f(t.opts,["#7c5cff","#22d3ee"]),a=t.width/2,r=t.height/2,i=Math.hypot(a,r);var o=Math.max(60,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var n=0;n=1)e[l]=r(),e[l].life=0;else{var h=Math.sin(s.life*Math.PI);n(i,s.x,s.y,s.size*h,s.col,h)}}i.globalCompositeOperation="source-over"}}}}),y("tunnel",{defaults:{rings:20,sides:6,speed:1,colors:["#22d3ee","#f472b6","#7c5cff"],background:"#03030b"},setup:function(t){var e,a,o;function i(){e=t.width/2,a=t.height/2,o=f(t.opts,["#22d3ee","#f472b6"])}function l(t,o,r,i){t.beginPath();for(var l=0;l<=i;l++){var s=r+l/i*n(),h=e+Math.cos(s)*o,c=a+Math.sin(s)*o*.78;0===l?t.moveTo(h,c):t.lineTo(h,c)}}return i(),{resize:i,draw:function(i,n){w(t,.28);for(var h=t.ctx,f=Math.hypot(e,a),d=0;d0&&(l+=e[v].vx,h+=e[v].vy,c+=e[v].x,f+=e[v].y,m<380&&(d-=g,p-=x),u++)}if(u&&(n.vx+=.04*(l/u-n.vx)+8e-4*(c/u-n.x)+.02*d,n.vy+=.04*(h/u-n.vy)+8e-4*(f/u-n.y)+.02*p),t.mouse.active){var b=t.mouse.x-n.x,y=t.mouse.y-n.y;n.vx+=.0012*b,n.vy+=.0012*y}var M=Math.hypot(n.vx,n.vy);M>2.4&&(n.vx=n.vx/M*2.4,n.vy=n.vy/M*2.4),n.x+=n.vx*t.opts.speed*1.6,n.y+=n.vy*t.opts.speed*1.6,n.x<0&&(n.x=t.width),n.x>t.width&&(n.x=0),n.y<0&&(n.y=t.height),n.y>t.height&&(n.y=0);var S=Math.atan2(n.vy,n.vx);r.fillStyle=s(n.col,.9),r.save(),r.translate(n.x,n.y),r.rotate(S),r.beginPath(),r.moveTo(5,0),r.lineTo(-4,3),r.lineTo(-4,-3),r.closePath(),r.fill(),r.restore()}}}}}),y("ribbons",{defaults:{count:5,speed:1,colors:["#7c5cff","#22d3ee","#f472b6"],background:"#05060f"},setup:function(t){var e,a;function r(){a=f(t.opts,["#7c5cff","#22d3ee"]),e=[];for(var r=0;rt.width||h.y<0||h.y>t.height)&&(e[l]=r())}}}}}),y("globe",{defaults:{count:320,speed:1,colors:["#22d3ee","#7c5cff"],background:"#05060f"},setup:function(t){var e,a,o,r,i;function l(){i=f(t.opts,["#22d3ee","#7c5cff"]),a=t.width/2,o=t.height/2,r=.4*Math.min(t.width,t.height);var n=t.opts.count;e=[];for(var l=Math.PI*(3-Math.sqrt(5)),s=0;st.width&&(e-=t.width,u=0,c.fillStyle=s(l(t.opts.background),1),c.fillRect(0,0,t.width,t.height)),c.fillStyle=s(l(t.opts.background),1),c.fillRect(e,0,26,t.height),c.strokeStyle=s(i,1),c.lineWidth=1,c.beginPath(),c.moveTo(u,f),c.lineTo(e,f),c.stroke();var v,g,x=f-(v=e%o/o,g=0,g+=.12*Math.exp(-Math.pow((v-.15)/.02,2)),g-=.16*Math.exp(-Math.pow((v-.27)/.008,2)),g+=1*Math.exp(-Math.pow((v-.3)/.007,2)),((g-=.38*Math.exp(-Math.pow((v-.34)/.008,2)))+.22*Math.exp(-Math.pow((v-.52)/.03,2)))*d);c.strokeStyle=s(r,1),c.lineWidth=2,c.shadowColor=s(r,.9),c.shadowBlur=8,c.beginPath(),c.moveTo(u,a),c.lineTo(e,x),c.stroke(),c.shadowBlur=0,a=x}}}}),y("equalizer",{defaults:{bars:32,speed:1,colors:["#7c5cff","#22d3ee","#34d399"],background:"#05060f"},setup:function(t){var e,a;function r(){a=f(t.opts,["#7c5cff","#22d3ee"]),e=[];for(var r=0;rMath.PI-.12&&(x.seen=1),x.seen=Math.max(0,x.seen-.4*h),x.seen>.01&&(c.fillStyle=s(d,x.seen),c.beginPath(),c.arc(e+Math.cos(x.a)*x.r,a+Math.sin(x.a)*x.r,3+2*x.seen,0,n()),c.fill())}}}}}),y("embers",{defaults:{count:120,speed:1,colors:["#fbbf24","#fb7185","#f59e0b"],background:"#0a0402"},setup:function(t){var e,a;function r(e){return{x:o(.2*t.width,.8*t.width),y:e?o(0,t.height):t.height+6,vy:o(.4,1.2),drift:o(0,n()),r:o(1,3),life:o(.5,1),col:a[Math.random()*a.length|0]}}function i(){a=f(t.opts,["#fbbf24","#fb7185"]);var o=Math.max(40,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var i=0;i=u.length&&(r="hold",i=1.4)):"hold"===r?(r="erase",i=.5):(o--,i=.03/t.opts.speed,o<=0&&(r="type",a=(a+1)%e.length,i=.3)));var v=u.slice(0,Math.max(0,o));p.font="700 "+f+"px "+("string"==typeof t.opts.fontFamily?t.opts.fontFamily:"Inter, system-ui, sans-serif"),p.textBaseline="middle";var g=p.measureText(v).width,x=t.width/2-g/2,m=t.height/2;p.fillStyle=s(h,1),p.textAlign="left",p.fillText(v,x,m),n%1<.5&&(p.fillStyle=s(c,1),p.fillRect(x+g+3,m-.45*f,.12*f,.9*f))}}}}),y("spirograph",{defaults:{speed:1,colors:["#7c5cff","#22d3ee","#f472b6"],background:"#05060f"},setup:function(t){var e,a,r,i,l,h,d,p,u,v,g;function x(){d=o(.6,1),p=o(.12,.42),u=o(.3,.9),i=0,l=null,g=o(8,16)}function m(){e=t.width/2,a=t.height/2,r=.32*Math.min(t.width,t.height),h=f(t.opts,["#7c5cff","#22d3ee","#f472b6"]),v=0,x()}function b(t){var o=(d-p)/p;return{x:e+((d-p)*Math.cos(t)+u*p*Math.cos(o*t))*r,y:a+((d-p)*Math.sin(t)-u*p*Math.sin(o*t))*r}}return m(),{resize:m,draw:function(e,a){w(t,.035);for(var o=t.ctx,r=0;r<7;r++){i+=.06*t.opts.speed,v+=.0016;var f=b(i);if(l){var d=c(h[Math.floor(v)%h.length],h[(Math.floor(v)+1)%h.length],v%1);o.strokeStyle=s(d,.8),o.lineWidth=1.4,o.beginPath(),o.moveTo(l.x,l.y),o.lineTo(f.x,f.y),o.stroke()}l=f,i>g*n()&&x()}}}}}),y("supernova",{defaults:{speed:1,colors:["#fff1c4","#ff9d5c","#7c5cff","#22d3ee"],background:"#04040c",particles:150},setup:function(t){var e,a,l,h,c,d,p,u;function v(){e=t.width/2,a=t.height/2,l=f(t.opts,["#fff1c4","#ff9d5c","#7c5cff"]),h=[],c="charge",d=0,p=0,u=.25}return v(),{resize:v,draw:function(f,v){w(t,.16);var g=t.ctx;g.globalCompositeOperation="lighter";var x=v*t.opts.speed;if("charge"===c){u=Math.min(1.7,u+.7*x),d+=x;var m=.045*Math.min(t.width,t.height)*u,b=.7+.3*Math.sin(22*f)*(u/1.7),y=g.createRadialGradient(e,a,0,e,a,6*m);y.addColorStop(0,s(l[0],.9*b)),y.addColorStop(.4,s(l[0],.22)),y.addColorStop(1,"rgba(0,0,0,0)"),g.fillStyle=y,g.beginPath(),g.arc(e,a,6*m,0,n()),g.fill(),d>2.3&&(c="burst",p=0,function(){h=[];for(var e=t.opts.particles,a=Math.min(t.width,t.height),r=0;r1.05&&(c="charge",d=0,u=.25)}g.globalCompositeOperation="source-over"}}}}),y("quasar",{defaults:{speed:1,colors:["#22d3ee","#7c5cff","#eef3ff"],background:"#04040c",particles:170},setup:function(t){var e,a,i,l,h,c;function d(){e=t.width/2,a=t.height/2,i=.06*Math.min(t.width,t.height),l=f(t.opts,["#22d3ee","#7c5cff","#eef3ff"]),h=[];for(var r=0;r1&&(M.p-=1);var S=a+M.dir*M.p*u,k=M.p*i*2.2,C=e+M.off*k,T=.8*(1-M.p);d.fillStyle=s(M.col,T),d.beginPath(),d.arc(C,S,r(2.2,.4,M.p),0,n()),d.fill()}var P=d.createRadialGradient(e,a,0,e,a,3*i);P.addColorStop(0,s(l[2]||l[0],.95)),P.addColorStop(.5,s(l[0],.3)),P.addColorStop(1,"rgba(0,0,0,0)"),d.fillStyle=P,d.beginPath(),d.arc(e,a,3*i,0,n()),d.fill(),d.globalCompositeOperation="source-over",d.fillStyle="#000",d.beginPath(),d.arc(e,a,.8*i,0,n()),d.fill()}}}}),y("starcluster",{defaults:{speed:1,count:280,colors:["#ffffff","#bcd4ff","#fff1c4","#ffd6a5"],background:"#04040c"},setup:function(t){var e,a,r,i,l,h;function c(){a=t.width/2,r=t.height/2,i=.34*Math.min(t.width,t.height),l=f(t.opts,["#ffffff","#bcd4ff"]),h=0,e=[];for(var s=t.opts.count,c=0;ct.width)&&(f.vx*=-1),(f.y<0||f.y>t.height)&&(f.vy*=-1)}i.globalCompositeOperation="lighter";for(var d=0;dt.width+2&&(f.x=-2),f.x<-2&&(f.x=t.width+2),f.y>t.height+2&&(f.y=-2),f.y<-2&&(f.y=t.height+2);var p=.4+.6*Math.sin(1.5*a+f.ph);l.fillStyle=s(f.col,(.1+.5*f.z)*p),l.beginPath(),l.arc(f.x,f.y,f.r*(.5+f.z),0,n()),l.fill()}l.globalCompositeOperation="source-over"}}}}),y("orrery",{defaults:{speed:1,rings:5,colors:["#c9a959","#22d3ee","#7c5cff","#eef3ff"],background:"#05060f"},setup:function(t){var e,a,r,i,l;function h(){e=t.width/2,a=t.height/2,r=.42*Math.min(t.width,t.height),i=f(t.opts,["#c9a959","#22d3ee"]),l=[];for(var s=t.opts.rings,h=0;h.7&&(r.strokeStyle=s(l.col,.12*f),r.lineWidth=1,r.beginPath(),r.arc(l.x,l.y,.9*h,0,n()),r.stroke())}r.globalCompositeOperation="source-over"}}}}),y("magnetosphere",{defaults:{speed:1,colors:["#22d3ee","#7c5cff","#a8d8ff"],background:"#04040c",lines:7},setup:function(t){var e,a,r,i,l;function h(){e=t.width/2,a=t.height/2,r=.12*Math.min(t.width,t.height),i=f(t.opts,["#22d3ee","#7c5cff"]),l=[];for(var n=0;n<72;n++)l.push({shell:1+(Math.random()*t.opts.lines|0),p:Math.random(),side:Math.random()<.5?1:-1,v:o(.3,.8),col:i[Math.random()*i.length|0]})}function c(t,o,i){var n=o*Math.PI,l=r*t*Math.sin(n)*Math.sin(n);return{x:e+i*l*Math.sin(n),y:a-l*Math.cos(n)}}return h(),{resize:h,draw:function(o,h){S(t);var f=t.ctx;f.globalCompositeOperation="lighter";var d=f.createRadialGradient(e,a,0,e,a,1.4*r);d.addColorStop(0,s(i[0],.5)),d.addColorStop(.6,s(i[1]||i[0],.18)),d.addColorStop(1,"rgba(0,0,0,0)"),f.fillStyle=d,f.beginPath(),f.arc(e,a,1.4*r,0,n()),f.fill(),f.fillStyle="#0a0e18",f.beginPath(),f.arc(e,a,r,0,n()),f.fill();for(var p=t.opts.lines,u=-1;u<=1;u+=2)for(var v=1;v<=p;v++){var g=1.4+.7*v;f.strokeStyle=s(i[1]||i[0],.12+.04*v),f.lineWidth=1,f.beginPath();for(var x=0;x<=40;x++){var m=c(g,x/40,u);0===x?f.moveTo(m.x,m.y):f.lineTo(m.x,m.y)}f.stroke()}for(var b=0;b1&&(y.p-=1);var M=c(1.4+.7*y.shell,y.p,y.side);f.fillStyle=s(y.col,.7*(.4+.6*Math.sin(y.p*Math.PI))),f.beginPath(),f.arc(M.x,M.y,1.6,0,n()),f.fill()}f.globalCompositeOperation="source-over"}}}}),x("lensing",{defaults:{mass:1,spin:.35,stars:1,speed:1,interactive:!0,colors:["#ffd9a0","#9fd0ff"],background:"#05060f"},staticTime:11,uniforms:function(t){return{uMass:i(t.mass,.25,3),uSpin:i(t.spin,-1,1),uStars:i(t.stars,.1,2),uSpeed:t.speed,uWarm:C(t,["#ffd9a0","#9fd0ff"],0),uCool:C(t,["#ffd9a0","#9fd0ff"],1),uBg:k(t.background||"#05060f")}},fragment:["float rlH(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }","vec2 rlH2(vec2 p){"," return fract(sin(vec2(dot(p, vec2(127.1,311.7)), dot(p, vec2(269.5,183.3)))) * 43758.5453);","}","float rlLayer(vec2 p, float sc, float sd, float dens, out float mag){"," vec2 g = p * sc + sd * 7.13;"," vec2 id = floor(g);"," vec2 f = fract(g) - 0.5;"," vec2 hh = rlH2(id + sd);"," mag = hh.x;"," if (rlH(id * 1.31 + sd * 3.7) > 0.34 * dens) return 0.0;"," float d = length(f - (hh - 0.5) * 0.62);"," float rr = mix(0.016, 0.052, mag * mag);"," float core = smoothstep(rr, rr * 0.15, d);"," float halo = 0.20 * exp(-d * d / (rr * rr * 5.0));"," return (core * 1.15 + halo) * mix(0.30, 1.0, mag);","}","float rlSky(vec2 p){ return 0.011 + 0.007 * sin(p.x * 6.0 + 1.3) * sin(p.y * 5.0 - 0.7); }","","void main(){"," vec2 res = uResolution;"," float mn = min(res.x, res.y);"," vec2 p = (gl_FragCoord.xy - 0.5 * res) / mn;"," float tt = uTime * uSpeed;"," vec2 bh = vec2(0.13 * sin(tt * 0.13), 0.08 * sin(tt * 0.11 + 1.7));"," if (uMouse.z > 0.5) bh = (uMouse.xy - 0.5 * res) / mn;",""," vec2 d = p - bh;"," float r = max(length(d), 1e-5);"," float rs = 0.050 * uMass;"," float bc = 2.598 * rs;"," float k = 20.0 * rs * rs;",""," vec2 src = d * (1.0 - k / (r * r)) + vec2(tt * 0.004, tt * 0.0016);"," float q = k / (r * r);"," float mu = min(1.0 / max(abs(1.0 - q * q), 0.02), 42.0);",""," float m0, m1, m2;"," float s = rlLayer(src, 8.0, 0.0, uStars, m0);"," s += 0.80 * rlLayer(src, 15.0, 3.0, uStars, m1);"," s += 0.55 * rlLayer(src, 27.0, 9.0, uStars, m2);"," float sky = rlSky(src);",""," vec3 starCol = mix(uCool, uWarm, m1 * m1);"," starCol = mix(starCol, vec3(1.0), 0.45 + 0.4 * m0);"," vec3 col = starCol * s * mu * 1.15;"," col += mix(uWarm, uCool, 0.25) * sky * mu * 1.45;",""," float rsh = length(d - vec2(uSpin * 0.30 * rs, 0.0));"," col *= smoothstep(bc * 0.99, bc * 1.05, rsh);",""," float ph = exp(-max(r - bc, 0.0) / (0.085 * bc));"," ph *= smoothstep(bc * 0.985, bc * 1.03, rsh);"," ph *= 1.0 + 0.8 * uSpin * (d.x / r);"," col += mix(uWarm, vec3(1.0), 0.35) * ph * 1.3;",""," col = 1.0 - exp(-col * 1.5);"," fragColor = vec4(min(vec3(1.0), uBg + col), 1.0);","}"].join("\n")}),x("accretionDisk",{defaults:{inclination:1.15,beta:.5,temperature:1,speed:1,colors:["#e01500","#ffa62b","#bfe0ff"],background:"#04050c"},staticTime:6,uniforms:function(t){var e=["#e01500","#ffa62b","#bfe0ff"];return{uInc:i(t.inclination,.15,1.48),uBeta:i(t.beta,.05,.85),uTemp:i(t.temperature,.3,2.5),uSpeed:t.speed,uC0:C(t,e,0),uC1:C(t,e,1),uC2:C(t,e,2),uBg:k(t.background||"#04050c")}},fragment:["vec3 adRamp(float x){"," x = clamp(x, 0.0, 1.0);"," return x < 0.5 ? mix(uC0, uC1, x * 2.0) : mix(uC1, uC2, (x - 0.5) * 2.0);","}","float adBand(float ph){ float w = fwidth(ph); return sin(ph) * exp(-0.3 * w * w); }","","const float RS = 0.045;","const float RIN = 3.0 * RS;","const float ROUT = 0.42;","","vec3 adDisk(vec2 dc, float si, float atten){"," float r = length(dc);"," float vis = smoothstep(RIN, RIN * 1.05, r) * smoothstep(ROUT, ROUT * 0.86, r);"," if (vis < 0.002) return vec3(0.0);"," float beta = min(uBeta * sqrt(RIN / r), 0.95);"," float gam = 1.0 / sqrt(1.0 - beta * beta);"," float cosl = (dc.x / r) * si;"," float dop = 1.0 / (gam * (1.0 - beta * cosl));"," float grav = sqrt(max(1.0 - RS / r, 0.02));"," float g = dop * grav;"," float g2 = g * g;"," float flux = pow(RIN / r, 3.0) * (1.0 - sqrt(RIN / r));"," float om = uSpeed * 1.15 * pow(RIN / r, 1.5);"," float a = atan(dc.y, dc.x) - om * uTime;"," float tex = 0.66 + 0.19 * adBand(a * 3.0 + r * 13.0)"," + 0.13 * adBand(a * 7.0 - r * 21.0 + 1.7)"," + 0.08 * adBand(a * 12.0 + r * 9.0);"," float temp = (pow(max(flux, 1e-4), 0.25) * g * uTemp - 0.24) * 2.5;"," return adRamp(temp) * (flux * tex * g2 * g2 * 15.0) * vis * atten;","}","","void main(){"," vec2 res = uResolution;"," float mn = min(res.x, res.y);"," vec2 p = (gl_FragCoord.xy - 0.5 * res) / mn;"," float ci = max(cos(uInc), 0.05), si = sin(uInc);"," float bc = 2.598 * RS;"," float scr = max(length(p), 1e-5);",""," vec2 b = p * (1.0 - 2.0 * RS * RS / (scr * scr));"," vec2 dc = vec2(b.x, -b.y / ci);"," vec3 col = adDisk(dc, si, 1.0);"," if (dc.y * si < 0.0) col *= smoothstep(bc * 0.97, bc * 1.05, scr);",""," float u = (scr - bc) / (0.5 * bc);"," if (u > 0.0 && u < 1.0) {"," vec2 dir = normalize(vec2(p.x, -p.y / ci));"," float w = (1.0 - u) * smoothstep(0.0, 0.12, u);"," col += adDisk(dir * (RIN + (ROUT - RIN) * pow(u, 2.6)), si, w * 1.1);"," }",""," col *= smoothstep(bc * 0.985, bc * 1.015, scr);"," float ring = exp(-max(scr - bc, 0.0) / (0.045 * bc));"," col += mix(uC1, vec3(1.0), 0.35) * ring * smoothstep(bc * 0.99, bc * 1.02, scr) * 1.15;"," col += uC1 * 0.05 * exp(-scr * 2.4);",""," col = 1.0 - exp(-col * 1.4);"," col = pow(col, vec3(0.75));"," fragColor = vec4(min(vec3(1.0), uBg + col), 1.0);","}"].join("\n")}),y("nBody",{defaults:{count:13,gravity:1,softening:1,trail:1,speed:1,colors:["#ff7a2f","#ffc978","#fff6e8","#bcd4ff"],background:"#05060f"},setup:function(t){var e,a,r,l,h,d,p,u,v,g,x,m,b,y,M,S,k,C,P,R,z,L=.0012,q=170,A=0,O=[.05,.1,.19,.4];function E(){var t,o;for(t=0;t4&&(.5*(h[t]*h[t]+d[t]*d[t])-C*a[0]/Math.sqrt(n)>0||n>26)&&I(t,o(.18,.62))}}function F(){y=f(t.opts,["#ff7a2f","#ffc978","#fff6e8","#bcd4ff"]),M=t.width/2,S=t.height/2,k=.54*Math.min(t.width,t.height),C=.9*(t.opts.gravity||1);var n,s=.021*i(t.opts.softening,.15,6);for(P=s*s,e=Math.max(4,Math.min(40,Math.round(t.opts.count))),a=[],r=[],l=[],h=[],d=[],p=[],u=[],v=[],g=[],m=[],b=[],x=0,A=0,z=0,n=0;nR&&(R=a[n]);for(n=0;n=L&&C<14;)W(),z-=L,C++;z>.4&&(z=0),h.globalCompositeOperation="lighter",h.lineCap="round";var T=i(t.opts.trail,0,1);for(f=1;f=O&&f<44;)F(),q-=O,f++;q>.3&&(q=0),(A+=i)>40&&G();var d=Math.sqrt(u*u+v*v);M=d*Math.pow(y/3,1/3),S=.5*(g*g+x*x)-R/Math.max(d,.001),h.globalCompositeOperation="lighter";var m=h.createRadialGradient(C,T,0,C,T,.2*P);m.addColorStop(0,s(k[2]||k[0],1)),m.addColorStop(.09,s(k[1]||k[0],.42)),m.addColorStop(.34,s(k[1]||k[0],.07)),m.addColorStop(1,"rgba(0,0,0,0)"),h.fillStyle=m,h.beginPath(),h.arc(C,T,.2*P,0,n()),h.fill();var b,z=C+u*P,L=T+v*P,E=M*M;for(b=0;b0?Math.pow(e,.25):.0475)>S?(t=Math.sqrt(.4/(c*c*c)),g=0,x=0):(t=.92*Math.sqrt(.4/(S*S*S)),g=i(1-(x+=b)/.9,0,1),x>.9&&(c=M,x=0,g=0)),d+=t*b,p[v=(v+1)%y]=d,u[v]=c>S?M/c:M/S*Math.exp(6.5*-x)}function T(t){var e=t/b,a=Math.floor(e),o=e-a;if(a<0&&(a=0,o=0),a>=3198)return!1;var r=(v-a+y+y)%y,i=(r-1+y)%y;return k[0]=p[r]+(p[i]-p[r])*o,k[1]=u[r]+(u[i]-u[r])*o,!0}function P(){e=f(t.opts,["#7fb2ff","#ff77b8","#ffffff"]),a=t.width/2,o=t.height/2,l=.5*Math.min(t.width,t.height),m=.00123*i(t.opts.decay,.1,6),c=M,d=0,h=0,g=0,x=0,v=0,p=[],u=[];for(var r=0;r=b&&v<600;)C(),h-=b,v++;h>.2&&(h=0),u.globalCompositeOperation="lighter";var x,m,y,S,P=.5*Math.hypot(t.width,t.height)/l,R=[];for(m=0;m<32;m++)R.push([]);for(x=0;x<4;x++){var z=x%2,L=x*Math.PI/2,q=0,A=0,O=!1,E=0;for(m=0;m<=170;m++){var I=m/170,W=.03+(P-.03)*I*I;if(T(W/.17)){var F=k[0]+L,G=k[1],_=a+Math.cos(F)*W*l,N=o+Math.sin(F)*W*l;if(O){var B=Math.abs(F-E),D=i(G/(.35+2*W)*.55*Math.exp(-B*B*4),0,1),H=Math.min(15,Math.floor(16*D*1.3));(H=Math.max(0,H))>0&&R[16*z+H].push(q,A,_,N)}q=_,A=N,E=F,O=!0}else O=!1}}u.lineCap="round";for(var j=0;j<2;j++)for(y=0;y<2;y++)for(S=1;S<16;S++){var U=R[16*y+S];if(U.length){var V=(S+.6)/16.32;for(u.strokeStyle=s(e[y]||e[0],j?V:.16*V),u.lineWidth=j?.8+.13*S:3.5+.4*S,u.beginPath(),m=0;m0){var tt=1-g;u.strokeStyle=s(X,.85*g),u.lineWidth=r(11,.5,tt),u.beginPath(),u.arc(a,o,tt*Math.hypot(a,o)*1.15,0,n()),u.stroke();var et=.55*l*g,at=u.createRadialGradient(a,o,0,a,o,et);at.addColorStop(0,s(X,g)),at.addColorStop(1,"rgba(0,0,0,0)"),u.fillStyle=at,u.beginPath(),u.arc(a,o,et,0,n()),u.fill()}u.globalCompositeOperation="source-over"}}}});var q=["float bvH1(vec3 p){"," p = fract(p * 0.1031);"," p += dot(p, p.zyx + 31.32);"," return fract((p.x + p.y) * p.z);","}","vec3 bvH3(vec3 p){"," p = fract(p * vec3(0.1031, 0.1030, 0.0973));"," p += dot(p, p.yxz + 33.33);"," return fract((p.xxy + p.yxx) * p.zyx);","}","float bvN(vec3 x){"," vec3 i = floor(x); vec3 f = fract(x);"," f = f * f * (3.0 - 2.0 * f);"," return mix(mix(mix(bvH1(i), bvH1(i + vec3(1.0, 0.0, 0.0)), f.x),"," mix(bvH1(i + vec3(0.0, 1.0, 0.0)), bvH1(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),"," mix(mix(bvH1(i + vec3(0.0, 0.0, 1.0)), bvH1(i + vec3(1.0, 0.0, 1.0)), f.x),"," mix(bvH1(i + vec3(0.0, 1.0, 1.0)), bvH1(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);","}","mat2 bvRot(float a){ float s = sin(a); float c = cos(a); return mat2(c, s, -s, c); }","float bvFbm4(vec3 p){"," float a = 0.5; float s = 0.0;"," for (int i = 0; i < 4; i++) {"," s += a * bvN(p);"," p.xy = bvRot(1.09) * p.xy;"," p = p * 2.04 + vec3(1.7, 9.2, 4.3);"," a *= 0.53;"," }"," return s * 1.03;","}","float bvFbm3(vec3 p){"," float s = 0.5 * bvN(p);"," p.xy = bvRot(1.09) * p.xy; p = p * 2.04 + vec3(1.7, 9.2, 4.3);"," s += 0.27 * bvN(p);"," p.xy = bvRot(1.09) * p.xy; p = p * 2.04 + vec3(1.7, 9.2, 4.3);"," s += 0.15 * bvN(p);"," return s * 1.09;","}","float bvFbm2(vec3 p){"," float s = 0.5 * bvN(p);"," p.xy = bvRot(1.09) * p.xy;"," s += 0.28 * bvN(p * 2.04 + vec3(1.7, 9.2, 4.3));"," return s * 1.28;","}","float bvHG(float c, float g){"," float g2 = g * g;"," return (1.0 - g2) / pow(max(1.0 + g2 - 2.0 * g * c, 1e-4), 1.5);","}","vec2 bvSph(vec3 ro, vec3 rd, float ra){"," float b = dot(ro, rd);"," float k = dot(ro, ro) - ra * ra;"," float h = b * b - k;"," if (h < 0.0) return vec2(1.0, -1.0);"," h = sqrt(h);"," return vec2(-b - h, -b + h);","}","vec3 bvTone(vec3 x){"," x = max(x, vec3(0.0));"," x = (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);"," return pow(clamp(x, 0.0, 1.0), vec3(0.90));","}","float bvDith(vec2 c){ return fract(sin(dot(c, vec2(12.9898, 78.233))) * 43758.5453); }","vec2 bvUV(){ return (gl_FragCoord.xy - 0.5 * uResolution) / min(uResolution.x, uResolution.y) * 2.0; }","vec2 bvMUV(){ return (uMouse.xy - 0.5 * uResolution) / min(uResolution.x, uResolution.y) * 2.0; }","float bvStars(vec3 rd, float amt){"," vec3 p = rd * 170.0;"," vec3 i = floor(p); vec3 f = fract(p) - 0.5;"," vec3 o = bvH3(i) - 0.5;"," float d = length(f - o * 0.72);"," float m = bvH1(i + 7.31);"," float br = smoothstep(0.93 - 0.10 * amt, 1.0, m);"," return br * exp(-d * d * 240.0) * (0.35 + 0.65 * m);","}"].join("\n");function A(t,e,a,o,r,i,n){t.font="10px ui-monospace, SFMono-Regular, Menlo, monospace",t.textAlign=n||"left",t.textBaseline="alphabetic",t.fillStyle=s(r,i),t.fillText(e,Math.round(a),Math.round(o)),t.textAlign="left"}function O(t,e,a,o,r,i,n,l){if(t.strokeStyle=s(i,n),t.lineWidth=l||1,t.beginPath(),a===r){var h=Math.round(a)+.5;t.moveTo(e,h),t.lineTo(o,h)}else if(e===o){var c=Math.round(e)+.5;t.moveTo(c,a),t.lineTo(c,r)}else t.moveTo(e,a),t.lineTo(o,r);t.stroke()}function E(t,e,a,o,r,i,n){t.strokeStyle=s(i,n),t.lineWidth=1,t.strokeRect(Math.round(e)+.5,Math.round(a)+.5,Math.round(o),Math.round(r))}function I(t,e){var a=l(t.opts.background||"#04060f");return{bg:a,grid:c(a,e[0],.22),axis:c(a,e[0],.5),label:c(e[0],{r:255,g:255,b:255},.5),hi:e[e.length-1],data:e[0]}}function W(t){var e=43758.5453*Math.sin(127.1*t+311.7);return e-Math.floor(e)}function F(){return 2*(Math.random()+Math.random()+Math.random()-1.5)}function G(t){var e,a,o,r=i(t,1e3,4e4)/100;return e=r<=66?255:i(329.698727446*Math.pow(r-60,-.1332047592),0,255),a=i(r<=66?99.4708025861*Math.log(r)-161.1195681661:288.1221695283*Math.pow(r-60,-.0755148492),0,255),o=r>=66?255:r<=19?0:i(138.5177312231*Math.log(r-10)-305.0447927307,0,255),{r:Math.round(e),g:Math.round(a),b:Math.round(o)}}function _(t){var e=0,a=0,o=0,r=1;return t>=380&&t<440?(e=-(t-440)/60,o=1):t<490?(a=(t-440)/50,o=1):t<510?(a=1,o=-(t-510)/20):t<580?(e=(t-510)/70,a=1):t<645?(e=1,a=-(t-645)/65):t<=781&&(e=1),t<420?r=.25+.75*(t-380)/40:t>700&&(r=.25+.75*(781-t)/81),{r:Math.round(255*Math.pow(i(e*r,0,1),.85)),g:Math.round(255*Math.pow(i(a*r,0,1),.85)),b:Math.round(255*Math.pow(i(o*r,0,1),.85))}}function N(t,e){var a=14387769/(t*e);return a>60?0:1/(Math.pow(.001*t,5)*(Math.exp(a)-1))}function B(t,e,a){var o=document.createElement(t);return e&&(o.className=e),null!=a&&(o.innerHTML=a),o}x("volumetricNebula",{defaults:{speed:1,density:1,absorption:1,anisotropy:.6,interactive:!0,colors:["#ff4f96","#31c9f5","#ffe6c2"],background:"#03030c"},staticTime:14,uniforms:function(t){return{uSpeed:z(t.speed,1,0,6),uDensity:z(t.density,1,.05,4),uAbsorb:2.9*z(t.absorption,1,.05,5),uAniso:z(t.anisotropy,.6,-.9,.9),uColA:R(t,0,"#ff4f96"),uColB:R(t,1,"#31c9f5"),uLight:R(t,2,"#ffe6c2"),uBg:P(t.background||"#03030c")}},fragment:[q,"float bvShape(vec3 p){"," vec3 q = p;"," q.xz = bvRot(0.62) * q.xz;"," q.xy = bvRot(-0.22) * q.xy;"," float r = length(q * vec3(0.52, 1.12, 0.78));"," return smoothstep(1.55, 0.10, r);","}","vec3 bvWarp(vec3 p, float t){"," return p + 0.30 * vec3(sin(p.y * 2.05 + t * 0.21),"," sin(p.z * 1.71 - t * 0.17),"," sin(p.x * 2.37 + t * 0.13));","}","float bvCurve(float f, float sh){"," return pow(max(f - 0.505, 0.0) * 4.6, 1.6) * sh;","}","float bvDens(vec3 p, float t){"," return bvCurve(bvFbm4(bvWarp(p, t) * 1.18 + vec3(0.0, -t * 0.035, t * 0.02)), bvShape(p));","}","float bvDensLo(vec3 p, float t){"," return bvCurve(bvFbm2(bvWarp(p, t) * 1.18 + vec3(0.0, -t * 0.035, t * 0.02)), bvShape(p));","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," float ca = t * 0.042;"," vec3 ro = vec3(0.0, 0.12 * sin(t * 0.06), -3.05);"," vec3 rd = normalize(vec3(uv * 0.58, 1.28));"," ro.xz = bvRot(ca) * ro.xz;"," rd.xz = bvRot(ca) * rd.xz;"," float tl = 0.09 * sin(t * 0.05);"," ro.yz = bvRot(tl) * ro.yz;"," rd.yz = bvRot(tl) * rd.yz;"," vec3 L = vec3(0.55 * sin(t * 0.11), 0.24 * sin(t * 0.083), 0.40 * cos(t * 0.11));"," if (uMouse.z > 0.5) {"," vec2 m = bvMUV();"," L = mix(L, vec3(m.x * 1.05, m.y * 0.90, 0.10), 0.85);"," }"," vec3 col = vec3(0.0);"," float T = 1.0;"," vec2 hit = bvSph(ro, rd, 2.05);"," if (hit.y > hit.x && hit.y > 0.0) {"," float t0 = max(hit.x, 0.0);"," float ds = (hit.y - t0) / 40.0;"," float tt = t0 + ds * dth;"," for (int i = 0; i < 40; i++) {"," if (T < 0.010) break;"," vec3 p = ro + rd * tt;"," float d = bvDens(p, t) * uDensity;"," vec3 pl = L - p;"," float dl = max(length(pl), 1e-4);"," if (d > 0.004) {"," vec3 ld = pl / dl;"," vec3 sp = p; float sh = 0.0;"," for (int j = 0; j < 3; j++) { sp += ld * 0.26; sh += bvDensLo(sp, t) * uDensity; }"," float shT = exp(-sh * 0.26 * uAbsorb * 1.5);"," float ph = bvHG(dot(rd, ld), uAniso);"," float fall = 1.9 / (0.16 + dl * dl);"," vec3 alb = mix(uColB, uColA, smoothstep(0.05, 0.75, d));"," vec3 S = alb * uLight * shT * ph * fall * 2.30"," + uColB * 0.055"," + uColA * 0.30 * smoothstep(0.45, 1.6, d);"," col += T * S * d * ds;"," T *= exp(-d * ds * uAbsorb);"," }"," col += T * uLight * (0.0065 / (0.0022 + dl * dl * dl)) * ds * 1.4;"," tt += ds;"," }"," }"," vec3 bg = uBg;"," bg += vec3(bvStars(rd, 0.6)) * vec3(0.92, 0.95, 1.0) * 1.7;"," bg += vec3(bvStars(rd * 2.4 + 11.0, 0.3)) * vec3(0.7, 0.8, 1.0) * 0.8;"," bg += mix(uColB, uColA, 0.4) * 0.030 * (1.0 - 0.45 * length(uv));"," col += T * bg;"," col = bvTone(col * 1.30);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),x("starSurface",{defaults:{speed:1,temperature:5200,granulation:1,spots:.55,interactive:!0,colors:["#ff5a1e","#ffd9a0"],background:"#05030a"},staticTime:11,uniforms:function(t){var e=z(t.temperature,5200,1800,16e3);return{uSpeed:z(t.speed,1,0,6),uGran:z(t.granulation,1,0,1),uSpots:z(t.spots,.55,0,1),uHot:L(1.06*e,1),uWarm:L(.62*e,.3),uFlare:R(t,0,"#ff5a1e"),uBg:P(t.background||"#05030a")}},fragment:[q,"vec3 bvWorley(vec3 p, float t){"," vec3 ip = floor(p); vec3 fp = fract(p);"," float f1 = 9.0; float f2 = 9.0; float id = 0.0;"," for (int z = -1; z <= 1; z++) {"," for (int y = -1; y <= 1; y++) {"," for (int x = -1; x <= 1; x++) {"," vec3 g = vec3(float(x), float(y), float(z));"," vec3 o = bvH3(ip + g);"," float d = length(g + 0.5 + 0.40 * sin(t + 6.2831853 * o) - fp);"," if (d < f1) { f2 = f1; f1 = d; id = o.y; } else if (d < f2) { f2 = d; }"," }"," }"," }"," return vec3(f1, f2, id);","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float R = 0.80;"," float r = length(uv);"," float px = 2.2 / min(uResolution.x, uResolution.y);"," vec3 col = uBg;"," vec3 rd3 = normalize(vec3(uv, 1.5));"," col += vec3(bvStars(rd3, 0.4)) * 1.1;"," float ang = atan(uv.y, uv.x);"," float cr = max(r / R, 1.0);"," float stream = 0.35 + 0.65 * bvFbm2(vec3(cos(ang) * 2.1, sin(ang) * 2.1, t * 0.04) * 1.9);"," col += uFlare * pow(1.0 / cr, 5.5) * stream * 0.22;"," col += vec3(1.0, 0.72, 0.42) * pow(1.0 / cr, 13.0) * 0.20;"," float disk = smoothstep(R + px, R - px, r);"," if (disk > 0.001) {"," float mu = sqrt(max(1.0 - (r / R) * (r / R), 0.0));"," vec3 sp = vec3(uv / R, mu);"," sp.yz = bvRot(-0.34) * sp.yz;"," sp.xz = bvRot(t * 0.030) * sp.xz;"," vec3 w1 = bvWorley(sp * 17.0, t * 1.00);"," vec3 w2 = bvWorley(sp * 41.0 + 17.0, t * 1.75);"," float lane1 = smoothstep(0.0, 0.20, w1.y - w1.x);"," float lane2 = smoothstep(0.0, 0.26, w2.y - w2.x);"," float cellB = 0.62 + 0.38 * w1.z;"," float pepper = 0.90 + 0.10 * bvN(sp * 90.0 + t * 0.6);"," float sgran = 0.80 + 0.30 * bvFbm2(sp * 4.6 + vec3(0.0, 0.0, t * 0.05));"," float cells = lane1 * cellB * (0.74 + 0.26 * lane2) * pepper * sgran;"," float gran = mix(0.72, cells, clamp(uGran, 0.0, 1.0));"," float sv = bvFbm3(sp * 2.30 + 31.0);"," float thr = 0.72 - 0.20 * uSpots;"," float pen = smoothstep(thr - 0.09, thr + 0.09, sv);"," float umb = smoothstep(thr + 0.05, thr + 0.15, sv);"," float fil = 0.45 + 0.55 * bvN(sp * 58.0 + t * 0.30);"," float spotDark = mix(1.0, mix(0.30 * fil + 0.05, 0.035, umb), pen);"," gran = mix(gran, 0.26 + 0.14 * gran, pen);"," float limb = (1.0 - 0.78 * (1.0 - mu)) * (0.42 + 0.58 * pow(mu, 0.40));"," vec3 surf = mix(uWarm, uHot, clamp(gran, 0.0, 1.0));"," surf += uHot * pow(clamp(gran, 0.0, 1.0), 6.0) * 0.42;"," surf *= limb * spotDark;"," surf += uHot * (1.0 - lane1) * pow(1.0 - mu, 2.4) * 0.55 * (1.0 - pen);"," if (uMouse.z > 0.5) {"," vec2 m = bvMUV();"," float pd = length(uv - m);"," float plage = exp(-pd * pd * 30.0) * disk;"," surf += uHot * plage * 0.9 + vec3(1.0, 0.93, 0.8) * plage * plage * 0.8;"," }"," col = mix(col, surf, disk);"," }"," float above = max(r - R, 0.0);"," col += uFlare * exp(-above * 170.0) * smoothstep(R - px * 2.0, R + px, r) * 1.1;"," float pr = 0.0;"," for (int i = 0; i < 3; i++) {"," float fi = float(i);"," float base = 1.15 + fi * 2.24 + t * 0.030;"," float span = 0.24 + 0.09 * sin(t * 0.21 + fi * 1.7);"," float ht = 0.17 + 0.09 * sin(t * 0.27 + fi * 2.3);"," vec2 upv = vec2(cos(base), sin(base));"," vec2 c1 = vec2(cos(base - span), sin(base - span)) * R;"," vec2 c2 = vec2(cos(base + span), sin(base + span)) * R;"," vec2 mid = 0.5 * (c1 + c2);"," float hw = 0.5 * length(c2 - c1);"," float rr = (hw * hw + ht * ht) / (2.0 * ht);"," vec2 cc = mid + normalize(mid) * (ht - rr);"," float dd = abs(length(uv - cc) - rr);"," float sect = smoothstep(cos(span * 3.1), cos(span * 1.5), dot(normalize(uv + 1e-5), upv));"," float wob = 0.30 + 0.90 * bvFbm2(vec3(uv * 9.0, t * 0.4 + fi * 9.0));"," float outside = smoothstep(R * 0.965, R * 1.01, r) * exp(-above * 5.5);"," pr += exp(-dd * dd * 1600.0) * sect * wob * outside;"," }"," col += uFlare * pr * 1.7 + vec3(1.0, 0.70, 0.46) * pr * pr * 0.8;"," col = bvTone(col * 1.02);"," col += (bvDith(gl_FragCoord.xy) - 0.5) / 220.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),x("atmosphere",{defaults:{speed:1,radius:1,thickness:.1,rayleigh:1,mie:1,interactive:!0,colors:["#6ff0d0","#3d7a52"],background:"#02030a"},staticTime:8,uniforms:function(t){return{uSpeed:z(t.speed,1,0,6),uRadius:z(t.radius,1,.25,3),uThick:z(t.thickness,.1,.02,.3),uRay:.42*z(t.rayleigh,1,0,4),uMie:.42*z(t.mie,1,0,4),uGlow:R(t,0,"#6ff0d0"),uLand:R(t,1,"#3d7a52"),uBg:P(t.background||"#02030a")}},fragment:[q,"void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," float R = 1.0;"," float Ra = R + uThick;"," float D = 1.0 + 0.92 / uRadius;"," vec3 ro = vec3(0.0, 0.0, -D);"," vec3 rd = normalize(vec3(uv, 1.55));"," rd.yz = bvRot(-0.60) * rd.yz;"," float sa = 1.32 + t * 0.15;"," float se = 0.20 + 0.12 * sin(t * 0.085);"," if (uMouse.z > 0.5) { vec2 m = bvMUV(); sa = 1.55 + m.x * 2.3; se = m.y * 0.95; }"," vec3 sun = normalize(vec3(sin(sa) * cos(se), sin(se), cos(sa) * cos(se)));"," vec2 ta = bvSph(ro, rd, Ra);"," vec2 tp = bvSph(ro, rd, R);"," bool onPlanet = (tp.y > tp.x) && (tp.x > 0.0);"," vec3 base = uBg;"," base += vec3(bvStars(rd, 0.55)) * 1.7;"," base += vec3(bvStars(rd * 2.6 + 5.0, 0.30)) * vec3(0.78, 0.85, 1.0) * 0.85;"," if (onPlanet) {"," vec3 hp = ro + rd * tp.x;"," vec3 n = normalize(hp);"," float ndl = dot(n, sun);"," float lam = max(ndl, 0.0);"," float land = bvFbm3(n * 1.95 + 4.0);"," float isLand = smoothstep(0.520, 0.575, land);"," vec3 alb = mix(vec3(0.008, 0.030, 0.086), uLand, isLand);"," alb = mix(alb, uLand * vec3(1.9, 1.45, 0.80), isLand * smoothstep(0.60, 0.70, land) * 0.8);"," float ice = smoothstep(0.76, 0.93, abs(n.y));"," alb = mix(alb, vec3(0.78, 0.83, 0.88), ice);"," float cl = smoothstep(0.585, 0.760, bvFbm3(n * 2.70 + vec3(t * 0.045, 0.0, 0.0)));"," alb = mix(alb, vec3(0.90, 0.92, 0.96), cl * 0.88);"," float spec = pow(max(dot(reflect(-sun, n), -rd), 0.0), 60.0) * (1.0 - isLand) * (1.0 - cl);"," base = alb * lam * 2.10 + vec3(1.0, 0.95, 0.86) * spec * 2.6;"," float city = smoothstep(0.64, 0.82, bvFbm3(n * 7.3 + 20.0)) * isLand * (1.0 - ice);"," base += vec3(1.0, 0.66, 0.30) * city * smoothstep(0.04, -0.26, ndl) * 0.95 * (1.0 - cl);"," }"," vec3 col = base;"," if (ta.y > ta.x && ta.y > 0.0) {"," float t0 = max(ta.x, 0.0);"," float t1 = onPlanet ? tp.x : ta.y;"," if (t1 > t0) {"," float ds = (t1 - t0) / 24.0;"," float tt = t0 + ds * dth;"," float Hr = uThick * 0.33;"," float Hm = uThick * 0.11;"," vec3 bR = vec3(5.80, 13.50, 33.10) * uRay;"," float bM = 21.0 * uMie;"," float odR = 0.0; float odM = 0.0; float glow = 0.0;"," vec3 sumR = vec3(0.0); vec3 sumM = vec3(0.0);"," for (int i = 0; i < 24; i++) {"," vec3 p = ro + rd * tt;"," float hh = length(p) - R;"," float rr = exp(-hh / Hr) * ds;"," float mm = exp(-hh / Hm) * ds;"," odR += rr; odM += mm;"," float nl = dot(normalize(p), sun);"," float ag = exp(-pow((hh - uThick * 0.36) / (uThick * 0.11), 2.0));"," glow += ag * smoothstep(0.18, -0.28, nl) * ds;"," vec2 tps = bvSph(p, sun, R);"," bool shadowed = (tps.y > tps.x) && (tps.x > 0.0);"," if (!shadowed) {"," vec2 tl = bvSph(p, sun, Ra);"," float dls = max(tl.y, 0.0) / 6.0;"," float lt = dls * 0.5;"," float lR = 0.0; float lM = 0.0;"," for (int j = 0; j < 6; j++) {"," float hj = length(p + sun * lt) - R;"," lR += exp(-hj / Hr) * dls;"," lM += exp(-hj / Hm) * dls;"," lt += dls;"," }"," vec3 att = exp(-(bR * (odR + lR) + bM * 1.1 * (odM + lM)));"," sumR += rr * att;"," sumM += mm * att;"," }"," tt += ds;"," }"," float cth = dot(rd, sun);"," float phR = 0.75 * (1.0 + cth * cth);"," float phM = bvHG(cth, 0.76) * 0.075;"," vec3 scat = (sumR * bR * phR + sumM * bM * phM) * 5.2;"," vec3 ext = exp(-(bR * odR + bM * odM));"," col = base * ext + scat + uGlow * glow * 5.5;"," }"," }"," col = bvTone(col * 1.08);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),x("dustLanes",{defaults:{speed:1,density:1,shadowSteps:4,extinction:1,interactive:!0,colors:["#ffd6a5","#8ec5ff"],background:"#04050e"},staticTime:12,uniforms:function(t){return{uSpeed:z(t.speed,1,0,6),uDensity:z(t.density,1,.05,4),uShadow:Math.round(z(t.shadowSteps,4,0,6)),uExt:2.45*z(t.extinction,1,.1,4),uWarm:R(t,0,"#ffd6a5"),uCool:R(t,1,"#8ec5ff"),uBg:P(t.background||"#04050e")}},fragment:[q,"float bvShapeD(vec3 p, float f){"," float body = smoothstep(0.505, 0.700, f);"," float fil = pow(1.0 - abs(f * 2.0 - 1.0), 6.0);"," float y = p.y + 0.62 * sin(p.x * 0.42 + 0.7) + 0.22 * sin(p.z * 0.5);"," float slab = exp(-y * y * 2.6);"," return (body * 1.15 + fil * 0.55) * slab * 2.25;","}","float bvDust(vec3 p, float t){"," vec3 q = p; q.x -= t * 0.17; q.y += t * 0.035;"," return bvShapeD(p, bvFbm4(q * 0.85));","}","float bvDustLo(vec3 p, float t){"," vec3 q = p; q.x -= t * 0.17; q.y += t * 0.035;"," return bvShapeD(p, bvFbm2(q * 0.85));","}","vec3 bvField(vec3 rd){"," vec3 n = normalize(rd);"," vec2 s = n.xy / max(n.z, 0.2);"," vec3 c = uBg;"," float b = (s.y + 0.05 + 0.10 * sin(s.x * 4.2)) * 4.6;"," c += uWarm * exp(-b * b) * 0.55;"," vec2 dc = s - vec2(0.13, -0.02);"," c += uWarm * exp(-dot(dc, dc) * 26.0) * 1.55;"," c += mix(uCool, vec3(1.0), 0.55) * exp(-dot(dc, dc) * 5.0) * 0.20;"," c += vec3(bvStars(n, 0.66)) * 2.4;"," c += vec3(bvStars(n * 2.7 + 9.0, 0.36)) * mix(uCool, vec3(1.0), 0.4) * 1.1;"," return c;","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," vec3 ro = vec3(0.30 * sin(t * 0.045), 0.08 * sin(t * 0.031), -2.6);"," vec3 rd = normalize(vec3(uv * 0.60, 1.35));"," rd.xz = bvRot(0.16 * sin(t * 0.10)) * rd.xz;"," vec3 L = vec3(-1.05, 0.75, 0.20);"," if (uMouse.z > 0.5) { vec2 m = bvMUV(); L = vec3(m.x * 1.7, m.y * 1.3, 0.15); }"," vec3 extTint = vec3(0.52, 0.82, 1.42);"," vec3 T = vec3(1.0);"," vec3 col = vec3(0.0);"," float ds = 0.115;"," float tt = 0.55 + ds * dth;"," for (int i = 0; i < 42; i++) {"," if (T.r < 0.008) break;"," vec3 p = ro + rd * tt;"," float d = bvDust(p, t) * uDensity;"," if (d > 0.004) {"," vec3 pl = L - p;"," float dl = max(length(pl), 1e-4);"," vec3 ld = pl / dl;"," float sh = 0.0;"," vec3 sp = p;"," for (int j = 0; j < 6; j++) {"," if (float(j) >= uShadow) break;"," sp += ld * 0.22;"," sh += bvDustLo(sp, t) * uDensity;"," }"," float shT = exp(-sh * 0.22 * uExt * 0.9);"," float ph = bvHG(dot(rd, ld), 0.42);"," float fall = 2.2 / (0.45 + dl * dl);"," vec3 scat = uCool * shT * ph * fall * 0.52 + uWarm * 0.05;"," col += T * scat * d * ds;"," T *= exp(-d * ds * uExt * extTint);"," }"," tt += ds;"," }"," col += T * bvField(rd);"," col = bvTone(col * 1.32);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),x("protoplanetary",{defaults:{speed:1,inclination:62,gaps:3,flare:.3,interactive:!0,colors:["#ffb066","#8fb6ff","#fff2dc"],background:"#03030b"},staticTime:16,uniforms:function(t){var e=z(t.inclination,62,0,88);return{uSpeed:z(t.speed,1,0,6),uElev:(90-e)*Math.PI/180,uGaps:Math.round(z(t.gaps,3,0,4)),uFlare:z(t.flare,.3,0,1),uColA:R(t,0,"#ffb066"),uColB:R(t,1,"#8fb6ff"),uStar:R(t,2,"#fff2dc"),uBg:P(t.background||"#03030b")}},fragment:[q,"const float BV_RIN = 0.30;","const float BV_ROUT = 2.45;","float bvH(float r){ return 0.055 * pow(max(r, 0.05), 1.0 + uFlare * 1.5); }","float bvDisk(vec3 p, float t){"," float r = length(p.xz);"," if (r < BV_RIN || r > BV_ROUT) return 0.0;"," float H = bvH(r);"," float z = p.y / H;"," float vert = exp(-0.5 * z * z);"," float sig = pow(r, -0.80)"," * smoothstep(BV_RIN, BV_RIN * 1.55, r)"," * smoothstep(BV_ROUT, BV_ROUT * 0.62, r);"," for (int i = 0; i < 4; i++) {"," float on = step(float(i) + 0.5, uGaps);"," float rg = 0.58 + float(i) * 0.46;"," float w = 0.062 + 0.016 * float(i);"," float g = (r - rg) / w;"," sig *= 1.0 - on * 0.95 * exp(-g * g);"," float go = (r - rg - w * 2.1) / (w * 1.5);"," sig *= 1.0 + on * 0.45 * exp(-go * go);"," }"," float a = atan(p.z, p.x) - t * 0.50 * pow(max(r, 0.18), -1.5);"," float tex = 0.50 + 0.50 * bvFbm2(vec3(cos(a) * r * 2.1, p.y * 2.2, sin(a) * r * 2.1) * 1.25);"," return sig * vert * tex * 5.0;","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," vec3 ro = vec3(0.0, 0.0, -5.4);"," vec3 rd = normalize(vec3(uv, 2.05));"," float elev = uElev;"," if (uMouse.z > 0.5) { vec2 m = bvMUV(); elev = clamp(elev + m.y * 0.45, 0.04, 1.52); }"," ro.yz = bvRot(elev) * ro.yz; rd.yz = bvRot(elev) * rd.yz;"," float spin = t * 0.020;"," ro.xz = bvRot(spin) * ro.xz; rd.xz = bvRot(spin) * rd.xz;"," vec3 col = uBg;"," col += vec3(bvStars(rd, 0.5)) * 1.6;"," col += vec3(bvStars(rd * 2.5 + 7.0, 0.28)) * vec3(0.8, 0.86, 1.0) * 0.75;"," float T = 1.0;"," vec3 acc = vec3(0.0);"," vec2 hit = bvSph(ro, rd, BV_ROUT + 0.15);"," if (hit.y > hit.x && hit.y > 0.0) {"," float t0 = max(hit.x, 0.0);"," float ds = (hit.y - t0) / 46.0;"," float tt = t0 + ds * dth;"," for (int i = 0; i < 46; i++) {"," if (T < 0.010) break;"," vec3 p = ro + rd * tt;"," float dd = dot(p, p);"," float d = bvDisk(p, t);"," if (d > 0.004) {"," float rr = max(sqrt(dd), 0.12);"," vec3 toStar = -p / rr;"," float irr = 1.0 / (0.20 + rr * rr * 0.55);"," float tau = 0.0;"," float sds = rr / 3.0;"," vec3 sp = p;"," for (int j = 0; j < 3; j++) { sp += toStar * sds; tau += bvDisk(sp, t); }"," float shade = exp(-tau * sds * 2.4);"," float ph = bvHG(dot(p / rr, -rd), 0.52);"," vec3 tint = mix(uColA, uColB, smoothstep(BV_RIN * 1.5, BV_ROUT * 0.80, rr));"," vec3 S = tint * (irr * shade * ph * 0.42 + 0.045);"," acc += T * S * d * ds;"," T *= exp(-d * ds * 2.35);"," }"," acc += T * uStar * exp(-dd * 150.0) * ds * 4.5;"," acc += T * uStar * (0.0016 / (0.004 + dd * dd)) * ds * 0.55;"," tt += ds;"," }"," }"," col = col * T + acc;"," col = bvTone(col * 1.16);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),y("spectrograph",{defaults:{temperature:5778,lines:!0,speed:1,interactive:!0,colors:["#22d3ee","#7c5cff"],background:"#04060f"},setup:function(t){for(var e,a,o,l,h,c,d,p,u,v,g,x,m,b,y=380,M={y:0,h:0},w={y:0,h:0},k=[{w:393.37,n:"Ca K",d:.72,s:1.7,tag:1},{w:396.85,n:"Ca H",d:.62,s:1.5,tag:0},{w:410.17,n:"Hδ",d:.38,s:1,tag:0},{w:434.05,n:"Hγ",d:.44,s:1.1,tag:0},{w:438.36,n:"Fe",d:.3,s:.7,tag:0},{w:486.13,n:"Hβ",d:.55,s:1.3,tag:1},{w:516.73,n:"Mg b",d:.5,s:1.5,tag:1},{w:527.04,n:"Fe",d:.32,s:.8,tag:0},{w:588.99,n:"Na D",d:.58,s:1,tag:1},{w:589.59,n:"Na D2",d:.52,s:1,tag:0},{w:630.25,n:"Fe",d:.24,s:.6,tag:0},{w:656.28,n:"Hα",d:.66,s:1.9,tag:1},{w:686.72,n:"O2",d:.3,s:.9,tag:0}],C=[],T=0;T<90;T++)C.push({w:y+320*W(3.1*T),d:.05+.17*W(7.7*T),s:.35+.5*W(5.3*T)});function P(t){return y+(t-o)/(l-o)*320}function R(t){return o+(t-y)/320*(l-o)}function z(){var r,n,s;for(e=f(t.opts,["#22d3ee","#7c5cff"]),a=I(t,e),o=34,l=t.width-12,M.y=25,M.h=Math.max(20,Math.round(.135*t.height)),w.y=M.y+M.h+22,w.h=Math.max(50,t.height-w.y-66),h=t.opts.temperature,b=Math.max(8,Math.round(l-o)),d=0,r=0;r<=320;r++){var S=N(y+r,h);S>d&&(d=S)}for(s=t.opts.temperature,c={balmer:.35+.9*Math.exp(-Math.pow(Math.log(s/9500)/.42,2)),metal:.8*i((7500-s)/3600,0,1)+.35},p=[],u=[],r=0;r=1)for(x-=1,v=g,g=[],f=0;f=3e4?"O":C>=1e4?"B":C>=7500?"A":C>=6e3?"F":C>=5200?"G":C>=3700?"K":"M")+"-type",l,15,a.label,.55,"right");var L=[];for(f=0;fl-8||(O(T,d,M.y+M.h,d,M.y+M.h+5,a.label,.6),A(T,k[f].n,d,M.y+M.h+16,a.label,.82,"center")));var G=w.y,N=w.h,B=1.12,D=[0,.5,1];for(f=0;fl-14;A(T,String(U),V?l:d,j+15,a.label,.6,V?"right":"center")}A(T,"WAVELENGTH (nm)",l,j+27,a.label,.42,"right"),!1!==t.opts.interactive&&t.mouse.active?(H=i((t.mouse.x-o)/(l-o),0,1),m=H):((m+=.055*c*z)>1.4&&(m-=1.4),H=i(m,0,1));var K=o+H*(l-o),J=P(K),X=i(Math.round(H*(b-1)),0,b-1);T.setLineDash([2,3]),O(T,K,M.y,K,G+N,a.hi,.75),T.setLineDash([]);var Y=G+N-i(L[X],0,B)/B*N;T.fillStyle=s(a.hi,1),T.beginPath(),T.arc(K,Y,2.6,0,n()),T.fill();var Q=null,Z=4.2;for(f=0;f=1?0:1-r*(1-Math.sqrt(1-t*t))}function z(t){if(t>=1+o)return 1;var e,a,i=0,l=o/13;for(e=0;e<13;e++){var s=(e+.5)*l;for(a=0;a<26;a++){var h=(a+.5)/26*n();i+=R(Math.sqrt(t*t+s*s+2*t*s*Math.cos(h)))*s}}return 1-(i*=l*(n()/26))/(Math.PI*(1-r/3))}function L(){e=f(t.opts,["#22d3ee","#7c5cff"]),a=I(t,e),o=i(t.opts.radiusRatio,.02,.35),r=i(t.opts.limbDarkening,0,.95),l=i(t.opts.impact,0,.95);var s=86400*(h=Math.max(.2,t.opts.period));d=Math.pow(6674e-14*1408*s*s/(3*Math.PI),1/3),p=Math.sqrt(Math.max(.02,(1+o)*(1+o)-l*l))+.55,g=24*h/Math.PI*Math.asin(i(Math.sqrt((1+o)*(1+o)-l*l)/d,-1,1)),u=[];var c,S=1;for(c=0;c<=C;c++){var R=2*p*(c/C)-p,L=z(Math.sqrt(R*R+l*l));u.push(L),L=1&&(b-=1,y=[],k=-1);var I=Math.floor(b*C),W=2*p*b-p,G=Math.sqrt(W*W+l*l),_=u[i(I,0,C)];if(I>k){for(x=Math.max(0,k+1);x<=I;x++)y.push({i:x,f:u[x]+F()*v*.055});k=I}A(L,"TRANSIT PHOTOMETRY",14,15,a.label,.9),A(L,"synthetic · linear limb-darkening law",t.width-12,15,a.label,.4,"right");var N=T.r;L.save(),L.beginPath(),L.arc(T.cx,T.cy,N,0,n()),L.clip();var B=L.createRadialGradient(T.cx,T.cy,0,T.cx,T.cy,N);for(x=0;x<=8;x++){var D=x/8,H=.88*R(Math.min(D,.999));B.addColorStop(D,"rgb("+Math.round(w.r*H)+","+Math.round(w.g*H)+","+Math.round(w.b*H)+")")}L.fillStyle=B,L.beginPath(),L.arc(T.cx,T.cy,N,0,n()),L.fill();var j=.16*e*t.opts.speed;for(x=0;x0?{r:255,g:245,b:220}:{r:90,g:45,b:20};Z.addColorStop(0,s($,.2*J*Math.abs(U.v)*2.1)),Z.addColorStop(1,s($,0)),L.fillStyle=Z,L.save(),L.translate(X,Y),L.scale(Math.max(.16,J),1),L.translate(-X,-Y),L.beginPath(),L.arc(X,Y,Q,0,n()),L.fill(),L.restore()}}L.restore(),L.strokeStyle=s(c(w,a.bg,.45),.9),L.lineWidth=1,L.beginPath(),L.arc(T.cx,T.cy,N-.5,0,n()),L.stroke();var tt=T.cx+W*N,et=T.cy-l*N;L.fillStyle=s({r:4,g:6,b:12},1),L.beginPath(),L.arc(tt,et,o*N,0,n()),L.fill(),L.strokeStyle=s(a.hi,.7),L.lineWidth=1,L.beginPath(),L.arc(tt,et,o*N+.5,0,n()),L.stroke(),L.setLineDash([2,4]),O(L,T.cx-p*N,et,T.cx+p*N,et,a.hi,.26),L.setLineDash([]);var at=T.cx+N+30,ot=at+116,rt=T.cy-27;function it(t,e,o,r,i){A(L,o,t,rt+18*e,a.label,.45),A(L,r,t+62,rt+18*e,i?a.hi:a.label,i?.95:.85)}it(at,0,"Rp/Rs",o.toFixed(3)),it(at,1,"DEPTH",(100*v).toFixed(2)+" %",1),it(at,2,"b",l.toFixed(2)),it(at,3,"u",r.toFixed(2)),it(ot,0,"P",h.toFixed(2)+" d"),it(ot,1,"a/Rs",d.toFixed(2)),it(ot,2,"T14",g.toFixed(2)+" h");var nt=W/(n()*d)*h*24;it(ot,3,"t-t0",(nt>=0?"+":"")+nt.toFixed(2)+" h",1),E(L,P.x,P.y,P.w,P.h,a.axis,.45);var lt=[1,1-.5*v,1-v];for(x=0;x3?2:st>1.4?1:.5,ct=-Math.floor(st/ht)*ht;ct<=st+1e-6;ct+=ht){var ft=(ct/st+1)/2;m=P.x+ft*P.w,O(L,m,P.y,m,P.y+P.h,a.grid,Math.abs(ct)<1e-6?.7:.3);var dt=(ct>0?"+":"")+(ht<1?ct.toFixed(1):String(Math.round(ct)));A(L,dt,m,P.y+P.h+14,a.label,.55,"center")}for(A(L,"h from mid-transit",P.x+P.w,P.y+P.h+27,a.label,.42,"right"),L.strokeStyle=s(a.hi,.35),L.lineWidth=1,L.beginPath(),x=0;x<=C;x++)m=P.x+x/C*P.w,z=q(u[x]),0===x?L.moveTo(m,z):L.lineTo(m,z);for(L.stroke(),L.fillStyle=s(a.data,.85),x=0;x1-a&&(r=.5-.5*Math.cos(Math.PI*(1-e)/a)),.16+.84*r}function T(e){n[l]=function(e){var a,r,i=[],n=Math.max(.05,t.opts.noise);for(a=0;ao/2&&(p-=o),p<-o/2&&(p+=o),i[a]+=c.a*d*Math.exp(-Math.pow(p/1.15,2))}}for(r=g.length-1;r>=0;r--){var x=g[r],m=e-x.t0;if(!(m<0))if(m>x.dur)g.splice(r,1);else if(x.wide){var b=1-m/x.dur;for(a=0;a.94&&g.push({t0:k,dur:W(5.1*S)>.72?.16:.62,a:.7,wide:W(5.1*S)>.72}),T(k)}P()}return R(),{resize:R,update:R,draw:function(e,i){S(t);var n,l,c,f=t.ctx,d=t.opts.speed;m+=i*d;for(var p=0;m>=x&&p<8;){if(m-=x,b+=x,p++,Math.random()<.012){var u=Math.random()>.72;g.push({t0:b+x,dur:u?.1+.12*Math.random():.45+.45*Math.random(),a:.62+.3*Math.random(),wide:u})}T(b)}p&&P(),A(f,"RADIO SPECTROGRAM",y.x,15,a.label,.9),A(f,o+" ch · "+8..toFixed(0)+" MHz · HI 1420",t.width-12,15,a.label,.45,"right"),f.save(),f.imageSmoothingEnabled=!1,f.drawImage(h,0,0,o,r,Math.round(y.x),Math.round(y.y),Math.round(y.w),Math.round(y.h)),f.restore(),E(f,y.x,y.y,y.w,y.h,a.axis,.55);for(var v=w-4,C=w+4,R=2*Math.ceil(v/2);R<=C;R+=2)O(f,l=y.x+(R-v)/8*y.w,y.y+y.h,l,y.y+y.h+4,a.axis,.85),A(f,String(Math.round(R)),l,y.y+y.h+15,a.label,.6,"center");A(f,"MHz",y.x+y.w,y.y+y.h+27,a.label,.45,"right");for(var z=r*x,L=z>8?4:z>4?2:1,q=0;q<=z+1e-6;q+=L)c=y.y+y.h-q/z*y.h,O(f,y.x-4,c,y.x,c,a.axis,.8),A(f,0===q?"now":"-"+q.toFixed(0)+"s",y.x-7,c+3.5,a.label,.55,"right");for(n=0;n<40;n++){var I=k(1-n/39);f.fillStyle=s(I,1),f.fillRect(M.x,y.y+n/40*y.h,M.w,y.h/40+1)}E(f,M.x,y.y,M.w,y.h,a.axis,.55);var W=[12,6,0];for(n=0;n0?"+":" ")+W[n],M.x+M.w+4,c+(0===n?8:n===W.length-1?0:3.5),a.label,.55);A(f,"dB",M.x,y.y+y.h+15,a.label,.5)}}}}),y("hrDiagram",{defaults:{count:150,evolve:1,speed:1,interactive:!0,colors:["#22d3ee","#7c5cff"],background:"#04060f"},setup:function(t){var e,a,o,l,h={x:0,y:0,w:0,h:0},c=Math.log(5772)/Math.LN10,d=[{c:"O",lo:3e4,hi:52e3},{c:"B",lo:1e4,hi:3e4},{c:"A",lo:7500,hi:1e4},{c:"F",lo:6e3,hi:7500},{c:"G",lo:5200,hi:6e3},{c:"K",lo:3700,hi:5200},{c:"M",lo:2300,hi:3700}];function p(t){return h.x+(1-(t-3.36)/(4.66-3.36))*h.w}function u(t){return h.y+h.h-(t- -4.8)/11.1*h.h}var v=.6,g=.645,x=.775,m=.8,b=.83;function y(t,e){var a,o,i,n,l=t.z;return t.m<=.8&&(e=(e%v+v)%v),e=.5?3.8*a:3.8*Math.log(.5)/Math.LN10+2.3*(a-Math.log(.5)/Math.LN10),lT:c+.25*e-.5*(t>=1?.6*a:.9*a)}}(a),l=Math.max(o.lL+.4,Math.min(3.55,o.lL+2.45))+.5*(W(13.7*t)-.5),s=3.7-.025*i(l,0,5.5)+.055*(W(19.1*t)-.5),h={m:a,z:o,tipL:l,tipT:s,tSub:s+.1,p:W(5.13*t),rate:.024*Math.pow(a,-.3),twp:W(8.9*t)*n(),tf:.5+1.1*W(3.3*t),tw:1,col:null,rr:1,sx:0,sy:0,lL:0,lT:0,ph:0,R:1};return h.m<=.8&&(h.p=W(5.13*t)*v),h}function w(){e=f(t.opts,["#22d3ee","#7c5cff"]),a=I(t,e),h.x=42,h.y=38,h.w=t.width-h.x-14,h.h=Math.max(60,t.height-h.y-58),o=[];for(var r=Math.max(20,Math.min(400,Math.round(t.opts.count))),i=0;i0?"+":"")+w,h.x-6,x+3.5,a.label,.55,"right");var k=[3e4,2e4,1e4,7e3,5e3,4e3,3e3];for(f=0;fh.x+h.w||(O(b,g,h.y,g,h.y+h.h,a.grid,.3),O(b,g,h.y+h.h,g,h.y+h.h+4,a.axis,.8),A(b,k[f]>=1e4?k[f]/1e3+"k":String(k[f]),g,h.y+h.h+15,a.label,.55,"center"));for(f=0;fh.x+h.w-5)){A(b,d[f].c,g,h.y-7,a.label,.7,"center");var T=p(Math.log(d[f].lo)/Math.LN10);T>h.x&&T cooler",h.x+h.w,h.y+h.h+28,a.label,.42,"right"),A(b,"GIANTS",p(3.56),u(3.15),a.label,.38),A(b,"MAIN SEQUENCE",p(3.96),u(-.95),a.label,.38),A(b,"WHITE DWARFS",p(4.5),u(-3.15),a.label,.38);var P=t.mouse.x,R=t.mouse.y,z=null,L=144;for(f=0;f=v&&(m.p-=v):m.p>=1&&(m.p-=1);var q=y(m,m.p);if(m.lL=q.lL,m.lT=q.lT,m.ph=q.ph,m.sx=p(q.lT),m.sy=u(q.lL),m.R=Math.pow(10,.5*q.lL-2*(q.lT-c)),!1!==t.opts.interactive&&t.mouse.active){var I=m.sx-P,W=m.sy-R,F=I*I+W*W;Fh.x+h.w+4||m.syh.y+h.h+4||(m.col=G(Math.pow(10,m.lT)),m.rr=i(1.4+Math.log(m.R)/Math.LN10*.8,1,3.3),m.tw=.8+.2*Math.sin(e*m.tf*t.opts.speed+m.twp),B.push(m));for(b.globalCompositeOperation="lighter",f=0;f=d[e].lo)return d[e].c;return"M"}(j)+" "+(_=m.ph,N=m.lL,(4===_?"D":3===_?"pAGB":2===_?N>4?"I":"III":1===_?"IV":"V")+" ")+j+" K",V="log L "+(m.lL>=0?"+":"")+m.lL.toFixed(2),K="M "+m.m.toFixed(2)+" R "+(m.R<10?m.R.toFixed(2):m.R.toFixed(0)),J=6.02*Math.max(U.length,V.length,K.length)+16,X=i(m.sx+12,h.x+2,h.x+h.w-J-2),Y=i(m.sy-46-10,h.y+2,h.y+h.h-46-2);b.strokeStyle=s(a.hi,.9),b.lineWidth=1,b.beginPath(),b.arc(m.sx,m.sy,7,0,n()),b.stroke(),b.fillStyle=s(a.bg,.94),b.fillRect(Math.round(X),Math.round(Y),Math.round(J),46),E(b,X,Y,J,46,a.hi,.75),A(b,U,X+8,Y+15,a.hi,.95),A(b,V,X+8,Y+28,a.label,.8),A(b,K,X+8,Y+41,a.label,.8)}}}}}),y("pulsarTiming",{defaults:{period:5.757451924,jitter:1.2,folds:600,speed:1,colors:["#22d3ee","#7c5cff"],background:"#04060f"},setup:function(t){var e,a,o,r,l,h,c,d,p,u,v,g,x,m=128,b={x:0,y:0,w:0,h:0},y={x:0,y:0,w:0,h:0},M=78;function w(t){var e=0;return e+=1*Math.exp(-Math.pow((t-.32)/.021,2)),(e+=.33*Math.exp(-Math.pow((t-.372)/.038,2)))+.21*Math.exp(-Math.pow((t-.79)/.027,2))}function k(t){return 2.4*Math.sin(n()*t/331)+.85*Math.sin(n()*t/47+1.1)}var C=!0;function T(){var t;for(t=0;tx&&(x=n)}var s=Math.max(12,Math.round(.28*Math.max(40,t.opts.folds)));for(i=0;i14?10:v>7?5:v>3.5?2:1,c=new Array(M),d=0,u=0,p=0,i=0;i=q){for(var W=0;W=.09&&(h=0,C=!0),p+=f*L;p>.42;)p-=.42,P(u+=5.4);var F=t.opts.period;A(z,"PSR J0437-4715",b.x,14,a.label,.9),A(z,"P "+F.toFixed(9)+" ms · DM 2.645",t.width-14,14,a.label,.55,"right");var G=b.y+b.h,_=b.h/(1.62*x);function N(t){return G-(t+.36*x)*_}E(z,b.x,b.y,b.w,b.h,a.axis,.45),O(z,b.x,N(0),b.x+b.w,N(0),a.grid,.8);for(var B=0;B<=1.0001;B+=.25)O(z,k=b.x+B*b.w,b.y,k,G,a.grid,.28),O(z,k,G,k,G+4,a.axis,.8),A(z,B.toFixed(2),k,G+15,a.label,.55,"center");for(A(z,"PULSE PHASE",b.x+b.w,G+29,a.label,.42,"right"),A(z,"flux",b.x-6,b.y+10,a.label,.5,"right"),z.fillStyle=s(a.hi,.32),z.fillRect(b.x,G+22,10,8),A(z,"one rotation",b.x+14,G+29,a.label,.5),O(z,b.x+104,G+26,b.x+114,G+26,a.data,.95,1.4),A(z,"folded mean",b.x+118,G+29,a.label,.5),z.fillStyle=s(a.hi,.13),z.beginPath(),z.moveTo(b.x,N(0)),w=0;w.02&&j<.24){var U=o[w]/Math.max(1,r);D+=U*U,H++}}function V(t){return y.y+y.h/2-t/v*(y.h/2-4)}D=Math.sqrt(D/Math.max(1,H)),A(z,"FOLDED PROFILE · "+r+" / "+q+" rotations",b.x,b.y-7,a.hi,.9),A(z,"S/N "+(x/Math.max(.001,D)).toFixed(1),b.x+b.w,b.y-7,a.label,.7,"right"),E(z,y.x,y.y,y.w,y.h,a.axis,.45),z.setLineDash([3,3]),O(z,y.x,V(0),y.x+y.w,V(0),a.grid,.9),z.setLineDash([]);for(var K=-Math.floor(v/g)*g;K<=v;K+=g)0!==K&&((R=V(K))y.y+y.h-3||(O(z,y.x,R,y.x+y.w,R,a.grid,.28),A(z,(K>0?"+":"")+K,y.x-6,R+3.5,a.label,.5,"right")));A(z,"TIMING RESIDUALS (us)",y.x,y.y-6,a.label,.6);var J=0,X=c[d].d;for(c[(d+M-1)%M].d,w=0;w=a-1?t[a-1]:c(t[o],t[o+1],e-o)}function a(t){return(t=i(t,0,1))*t*(3-2*t)}function h(t,e){return 1-Math.exp(-t*e)}var d={r:255,g:255,b:255};y("gravityWell",{defaults:{count:700,mass:1,softening:.09,trail:.15,speed:1,colors:["#12266b","#2563eb","#22d3ee","#a7f3d0","#fde68a","#fb7185"],background:"#04050e",interactive:!0},setup:function(t){var a,r,l,h,p,u,v,g=[],x=!0,m=0,b=0,y=0,M=0,k=0,C=0,T=[];function P(t,e){var a=m-t,o=b-e,r=a*a+o*o+p*p,i=l/(r*Math.sqrt(r));k=a*i,C=o*i}function R(t){return t*h/Math.pow(t*t+p*p,.75)}function z(t){var e=r*(.32+.98*Math.pow(Math.random(),.72)),a=i(.24+o(-.07,.07),.02,.45),s=e/r*2.6+o(-.09,.09),h=Math.random()*n(),c=e*(1-a*a),f=c/(1+a*Math.cos(h)),d=s+h,p=Math.sqrt(l/c),u=p*a*Math.sin(h),v=p*(1+a*Math.cos(h)),g=Math.cos(d),x=Math.sin(d);t.x=m+f*g,t.y=b+f*x,t.vx=u*g-v*x,t.vy=u*x+v*g,t.px=t.x-.05*t.vx,t.py=t.y-.05*t.vy,t.r=o(.45,1.5),P(t.x,t.y),t.ax=k,t.ay=C}function L(){a=f(t.opts,["#12266b","#2563eb","#22d3ee","#fde68a","#fb7185"]),r=.34*Math.min(t.width,t.height),m=t.width/2,b=t.height/2,y=0,M=0,p=Math.max(2.5,(t.opts.softening||.09)*r),l=4*(t.opts.mass||1)*Math.PI*Math.PI*r*r*r/33.64,h=Math.sqrt(l),u=R(1.7*r),v=R(.3*r)-u;var o=Math.max(300,Math.round(t.opts.count*t.width*t.height/921600));g=[];for(var i=0;ir*r*12)z(f);else{var G=Math.sqrt(f.vx*f.vx+f.vy*f.vy),_=Math.pow(i((G-u)/v,0,1),.8);R=Math.min(11,12*_|0);var N=T[R];N.seg.push(f.px,f.py,f.x,f.y),_>.34&&N.dot.push(f.x,f.y,f.r)}}for(q.globalCompositeOperation="lighter",R=0;R<12;R++){var B=T[R],D=R/11;if(B.seg.length){for(q.strokeStyle=s(B.col,.17+.5*D),q.lineWidth=.6+1*D,q.beginPath(),L=0;La-1.002&&(e=a-1.002),o<0?o=0:o>r-1.002&&(o=r-1.002);var i=0|e,n=0|o,l=e-i,s=o-n,h=n*a+i,c=h+a;return(t[h]*(1-l)+t[h+1]*l)*(1-s)+(t[c]*(1-l)+t[c+1]*l)*s}var A=[0,0];function O(e,a,o){var r=e*d,i=a*d,n=7.4*(null==t.opts.swirl?1:t.opts.swirl),l=r+.3*o,s=i-.23*o,h=1.9*r-.17*o,c=1.7*i+.21*o,f=3.7*r+.13*o,p=3.2*i-.15*o,u=-Math.sin(l)*Math.sin(s)-.94*Math.sin(h)*Math.sin(c)-.9*Math.sin(f)*Math.sin(p),v=Math.cos(l)*Math.cos(s)+1.05*Math.cos(h)*Math.cos(c)+1.04*Math.cos(f)*Math.cos(p);A[0]=u*n,A[1]=-v*n}function E(t,e,o,i,n,l){var s,h,c=o*o,f=Math.max(0,Math.floor(t-2.3*o)),d=Math.min(a-1,Math.ceil(t+2.3*o)),x=Math.max(0,Math.floor(e-2.3*o)),m=Math.min(r-1,Math.ceil(e+2.3*o));for(h=x;h<=m;h++)for(s=f;s<=d;s++){var b=s-t,y=h-e,M=(b*b+y*y)/c;if(!(M>5.3)){var w=Math.exp(-M),S=h*a+s;v[S]+=i*w,g[S]+=(n-g[S])*Math.min(.9,.8*w),l&&(p[S]+=-y*l*w,u[S]+=b*l*w)}}}function I(e,o){var n,l,s,c=h(o,1.15);for(l=0;l.001){for(var I=1;I5e-4?1/b:0,G=i(A*W,-46,46),_=i(O*W,-46,46),N=((L+=(.03*E+.1)*Math.max(b,.008))%1+1)%1,B=.03*a+1.2+Math.min(3,.4*E),D=Math.min(1,9*b),H=B*B,j=Math.max(0,Math.floor(y-2.3*B)),U=Math.min(a-1,Math.ceil(y+2.3*B)),V=Math.max(0,Math.floor(q-2.3*B)),K=Math.min(r-1,Math.ceil(q+2.3*B));for(d=V;d<=K;d++)for(f=j;f<=U;f++){var J=f-y,X=d-q,Y=(J*J+X*X)/H;if(!(Y>5.3)){var Q=Math.exp(-Y);v[x=d*a+f]+=Q*(1.1+Math.min(3.4,1.1*E))*Math.max(b,.006)*4.5,g[x]+=(N-g[x])*Q*.5,p[x]+=(G-p[x])*Q*D+-X*Q*D*2.2,u[x]+=(_-u[x])*Q*D+J*Q*D*2.2}}P=y,R=q}else z=!1;b>0&&(F(o,b),I(o,b));var Z=k.data;for(x=0;x1.06*N&&(s.out=!0),s.shock>0&&(s.shock-=2.4*e),G>.02&&I<1.3*_&&I>.55*_){var B=(z*S+O*R)/I,D=Math.abs(B);if(D>.34&&D<.95&&Math.random()<22*e){var H=B>0;!H&&Math.random()<.62&&(H=!0);var j=z/I*u.x+O/I*u.y,U=z/I*v.x+O/I*v.y,V=Math.atan2(U,j),K=L(H?1:-1,V,k);s.st=1,s.fp=0,s.pole=H?1:-1,s.f0x=s.x,s.f0y=s.y,s.tgx=K.x,s.tgy=K.y;var J=.5*(s.x+K.x)-r,X=.5*(s.y+K.y)-l;s.c1x=r+2.15*J,s.c1y=l+2.15*X,s.bin=(Math.floor((V+Math.PI)/n()*C)%C+C)%C;continue}}var Y=W*W+F*F,Q=_*_/(Y*Y),Z=1/(1+Math.max(0,W)/(2*_)),$=h*(1-Q*(W*W-F*F)*Z),tt=h*(2*-Q*W*F*Z),et=$*m+tt*f,at=$*b+tt*x,ot=Math.min(1,7*e);s.vx+=(et-s.vx)*ot,s.vy+=(at-s.vy)*ot,s.out||(s.vx+=o(-1,1)*h*.1,s.vy+=o(-1,1)*h*.1),s.x+=s.vx*e,s.y+=s.vy*e,z=s.x-r,O=s.y-l,(I=Math.sqrt(z*z+O*O)||.001)<_&&(s.x=r+z/I*_,s.y=l+O/I*_);var rt=d+40;(s.x<-rt||s.x>t.width+rt||s.y<-rt||s.y>t.height+rt)&&A(s,!1)}else{s.fp+=2*e*(t.opts.speed||1);var it=Math.min(1,s.fp),nt=1-it;if(s.x=nt*nt*s.f0x+2*nt*it*s.c1x+it*it*s.tgx,s.y=nt*nt*s.f0y+2*nt*it*s.c1y+it*it*s.tgy,s.fp>=1){var lt=s.pole>0?T:P;lt[s.bin]+=2.2,lt[(s.bin+1)%C]+=.9,lt[(s.bin+C-1)%C]+=.9,A(s,!1)}}E(T,e),E(P,e)}function W(t,o,r,l,h,c,f){var d,p=null;for(d=0;d<=C;d++){var u=d%C,v=L(o,u/C*n(),l),g=v.x,x=v.y,m=i(5*(v.z+.04),0,1),b=r[u]*c*m;p&&(m>.01||p.v>.01)&&(t.strokeStyle=s(e(a,i(.03+.22*b,0,1)),i(b*f,0,.9)),t.lineWidth=h*(.55+.42*i(b,0,3)),t.beginPath(),t.moveTo(p.x,p.y),t.lineTo(g,x),t.stroke()),p={x:g,y:x,v:m}}}function F(t,e,a,o){var r;for(t.beginPath(),r=0;r0&&I(v),u.globalCompositeOperation="lighter";for(var _=-b,N=m,B=0;B<2;B++){for(u.beginPath(),p=0;p<=90;p++){var D=-Math.PI+p/90*n(),H=Math.cos(D),j=Math.sin(D),U=q(H)*(B?1.28:1)+(B?.24*c:0),V=r+(-m*H+_*j)*U,K=l+(-b*H+N*j)*U;0===p?u.moveTo(V,K):u.lineTo(V,K)}u.strokeStyle=s(a[B?3:1]||a[0],B?.2:.3),u.lineWidth=B?1.3:1,u.stroke()}var J=[[],[],[]];for(f=0;f0?1:0].push(d.px,d.py,d.x,d.y);for(var X=[a[1]||a[0],a[3]||a[0],a[0]],Y=[.72,.86,1],Q=[1.5,1.9,2.5],Z=0;Z<3;Z++){var $=J[Z];if($.length){for(u.strokeStyle=s(X[Z],Y[Z]),u.lineWidth=Q[Z],u.beginPath(),f=0;f<$.length;f+=4)u.moveTo($[f],$[f+1]),u.lineTo($[f+2],$[f+3]);u.stroke()}}u.globalCompositeOperation="source-over",u.fillStyle="#060911",u.beginPath(),u.arc(r,l,c,0,n()),u.fill();var tt=u.createRadialGradient(r-m*c*.7,l-b*c*.7,.1*c,r-m*c*.2,l-b*c*.2,1.5*c);tt.addColorStop(0,s(a[1]||a[0],.17)),tt.addColorStop(.45,s(a[2]||a[0],.05)),tt.addColorStop(1,"rgba(0,0,0,0)"),u.fillStyle=tt,u.beginPath(),u.arc(r,l,c,0,n()),u.fill(),u.globalCompositeOperation="lighter";var et=u.createRadialGradient(r,l,.93*c,r,l,1.26*c);et.addColorStop(0,s(a[1]||a[0],.26)),et.addColorStop(1,"rgba(0,0,0,0)"),u.fillStyle=et,u.beginPath(),u.arc(r,l,1.26*c,0,n()),u.fill(),W(u,1,T,k,8,1,.1),W(u,1,T,k,3,1,.3),W(u,1,T,k,1.1,1,.6),W(u,-1,P,k,6,.85,.1),W(u,-1,P,k,2.2,.85,.34),u.strokeStyle=s(a[0],.22),u.lineWidth=1,F(u,1,T,k),u.strokeStyle=s(a[0],.14),F(u,-1,P,k),u.globalCompositeOperation="source-over"}}}}),y("starForge",{defaults:{maxSystems:4,formation:1,speed:1,colors:["#fde68a","#22d3ee","#a78bfa","#fb7185"],background:"#03040c",interactive:!0},setup:function(t){var e,l=[],h=[],p=[],u=!0,v=0,g=0,x=0,m=!1,b=5,y=1,M=[{c:{r:255,g:142,b:92},s:.7},{c:{r:255,g:194,b:116},s:.86},{c:{r:255,g:240,b:206},s:1},{c:{r:210,g:228,b:255},s:1.22},{c:{r:172,g:204,b:255},s:1.42}];function k(t){return t<.3?c({r:182,g:140,b:108},{r:228,g:176,b:118},Math.random()):t<.58?c({r:104,g:170,b:150},{r:118,g:184,b:228},Math.random()):c({r:148,g:200,b:234},{r:208,g:226,b:248},Math.random())}function C(e,a,r){var i,l=o(.6,1)*Math.min(t.width,t.height)*.3,s={x:e,y:a,age:r,life:o(20,32),disk:l,incl:o(.18,.6),rot:Math.random()*Math.PI,spin:Math.random()<.5?1:-1,star:M[Math.random()*M.length|0],motes:[],planets:[],vx:o(-3,3),vy:o(-2.2,2.2)};for(i=0;i<78;i++){var h=l*o(.26,1.14);s.motes.push({a:Math.random()*n(),r0:l*o(1.7,3.6),rt:h,w:s.spin*(.95/Math.pow(h/l,1.5))*o(.85,1.15),br:o(.3,1.05)})}var c=3+(4*Math.random()|0),f=l*o(.26,.34);for(i=0;i1.18*l)break;var p=d/(1.18*l);s.planets.push({r:d,a:Math.random()*n(),w:s.spin*(1.05/Math.pow(d/l,1.5))*.55,sz:(1+2*Math.random())*(.5+p)*y,col:k(p),ring:p>.5&&Math.random()<.42,born:2.6+.46*i})}return s}function T(){e=f(t.opts,["#fde68a","#22d3ee","#a78bfa","#fb7185"]),y=Math.min(t.width,t.height)/300,p=[];for(var a=Math.round(t.width*t.height/3600),r=0;rt.width+6&&(W.x-=t.width+12),W.y<-6&&(W.y+=t.height+12),W.y>t.height+6&&(W.y-=t.height+12),R.moveTo(W.x+W.r,W.y),R.arc(W.x,W.y,W.r,0,n())}if(R.fillStyle=s({r:168,g:186,b:230},.3),R.fill(),A){(v+=z*(.3+Math.min(1.5,.004*I)))>=1&&l.length<(t.opts.maxSystems||4)&&(l.push(C(O,E,0)),v=0);var D=4+24*v,H=R.createRadialGradient(O,E,0,O,E,D);H.addColorStop(0,s(e[0],.5+.4*v)),H.addColorStop(.4,s(e[2]||e[0],.18)),H.addColorStop(1,"rgba(0,0,0,0)"),R.fillStyle=H,R.beginPath(),R.arc(O,E,D,0,n()),R.fill(),R.strokeStyle=s(e[0],.2+.45*v),R.lineWidth=1,R.beginPath(),R.arc(O,E,.55*D+3,-Math.PI/2,-Math.PI/2+n()*i(v,.02,1)),R.stroke(),g=O,x=E}else v>0&&(v=Math.max(0,v-.5*z));if((b-=z)<=0&&(b=o(7,13),l.length<(t.opts.maxSystems||4))){var j=function(){for(var e=0,a=0,r=-1,i=0;i<14;i++){for(var n=o(.15*t.width,.85*t.width),s=o(.18*t.height,.82*t.height),h=1e9,c=0;cr&&(r=h,e=n,a=s)}return{x:e,y:a}}();l.push(C(j.x,j.y,0))}for(k=l.length-1;k>=0;k--)if((P=l[k]).age+=z*L,P.x+=P.vx*z,P.y+=P.vy*z,P.age>P.life)l.splice(k,1);else{var U=a(P.age/.3)*a((P.life-P.age)/4.5),V=Math.cos(P.rot),K=Math.sin(P.rot),J=a(P.age/1),X=i((P.age-1)/.32,0,1),Y=U*(P.age<6?a(P.age/1.3):Math.max(.1,1-(P.age-6)/6));if(Y>.01&&P.age>.55){R.save(),R.translate(P.x,P.y),R.rotate(P.rot),R.scale(1,P.incl);var Q=1.2*P.disk,Z=R.createRadialGradient(0,0,.14*P.disk,0,0,Q);Z.addColorStop(0,s(c(P.star.c,e[1]||e[0],.45),.2*Y)),Z.addColorStop(.42,s(c(e[2]||e[0],P.star.c,.35),.11*Y)),Z.addColorStop(1,"rgba(0,0,0,0)"),R.fillStyle=Z,R.beginPath(),R.arc(0,0,Q,0,n()),R.fill(),R.restore()}for(R.beginPath(),T=0;Tot.born){var rt=.08*P.disk*i((P.age-ot.born)/1.4,0,1),it=Math.abs(tt-ot.r);it1&&P.age<2.6){var pt=(P.age-1)/1.6,ut=1-pt,vt=P.disk*(.18+2.3*pt);R.strokeStyle=s({r:255,g:248,b:230},.55*ut*ut*U),R.lineWidth=2.6*ut+.4,R.beginPath(),R.ellipse(P.x,P.y,vt,vt*r(1,P.incl,.5),P.rot,0,n()),R.stroke();var gt=1.7*P.disk*Math.sin(Math.PI*i(1.15*pt,0,1)),xt=-K,mt=V,bt=R.createLinearGradient(P.x-xt*gt,P.y-mt*gt,P.x+xt*gt,P.y+mt*gt);bt.addColorStop(0,"rgba(0,0,0,0)"),bt.addColorStop(.5,s(e[1]||e[0],.4*U*ut)),bt.addColorStop(1,"rgba(0,0,0,0)"),R.strokeStyle=bt,R.lineWidth=2.4*y*ut+.6,R.beginPath(),R.moveTo(P.x-xt*gt,P.y-mt*gt),R.lineTo(P.x+xt*gt,P.y+mt*gt),R.stroke()}for(T=0;T.002){var qt=(1.9+3*P.star.s)*y*(.55+.45*X),At=1+.7*Math.exp(3.4*-Math.max(0,P.age-1)),Ot=5.5*qt*At,Et=R.createRadialGradient(P.x,P.y,0,P.x,P.y,Ot);Et.addColorStop(0,s(P.star.c,.6*Lt)),Et.addColorStop(.22,s(P.star.c,.14*Lt)),Et.addColorStop(1,"rgba(0,0,0,0)"),R.fillStyle=Et,R.beginPath(),R.arc(P.x,P.y,Ot,0,n()),R.fill(),R.fillStyle=s(c(P.star.c,d,.55),.95*Lt),R.beginPath(),R.arc(P.x,P.y,qt*At,0,n()),R.fill()}}R.globalCompositeOperation="source-over"}}}}),x("relativisticJets",{defaults:{beta:.96,tilt:.75,knots:6,speed:1,colors:["#dbeafe","#7c5cff","#ffb168"],background:"#03040d",interactive:!0},staticTime:9,uniforms:function(e,a){var o=a.__rjets;o||(o=a.__rjets={cx:Math.cos(e.tilt||0),sy:Math.sin(e.tilt||0),psi:.35,lt:a.t});var r=i(a.t-o.lt,0,.06);o.lt=a.t;var n=(e.tilt||0)+.52*Math.sin(.085*a.t),s=.6*Math.sin(.055*a.t+.7);if(a.mouse.active&&a.width>2){var c=a.mouse.x/a.width*2-1,f=-(a.mouse.y/a.height*2-1);n=Math.atan2(f,c),s=1.02*(1-Math.min(1,Math.sqrt(c*c+f*f)/.92))}var d=h(r,5.5);o.cx+=(Math.cos(n)-o.cx)*d,o.sy+=(Math.sin(n)-o.sy)*d;var p=Math.sqrt(o.cx*o.cx+o.sy*o.sy)||1;o.cx/=p,o.sy/=p,o.psi+=(s-o.psi)*h(r,3.4);var u=Math.cos(o.psi),v=e.colors&&e.colors.length?e.colors:["#dbeafe","#7c5cff","#ffb168"],g=l(e.background||"#03040d");return{uSpeed:null==e.speed?1:e.speed,uBeta:i(null==e.beta?.96:e.beta,0,.998),uKnots:i(null==e.knots?6:e.knots,1,8),uAxis:[o.cx*u,o.sy*u,Math.sin(o.psi)],uTint:t(v[0]),uSheath:t(v[1]||v[0]),uTorus:t(v[2]||v[0]),uBg:[g.r/255,g.g/255,g.b/255]}},fragment:["float h11(float n){ return fract(sin(n*127.1)*43758.5453123); }","float h21(vec2 p){ return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453123); }","float sq(float x){ return x*x; }","float starfield(vec2 p){"," vec2 g=floor(p*40.0); float r=h21(g);"," vec2 c=(g+vec2(h21(g+1.3),h21(g+7.1)))/40.0;"," return smoothstep(0.0038,0.0,length(p-c))*step(0.955,r)*(0.3+0.7*h21(g+3.7));","}","void main(){"," float mn = min(uResolution.x, uResolution.y);"," vec2 p = (gl_FragCoord.xy - 0.5*uResolution)/mn;"," float t = uTime*uSpeed;"," vec3 n = normalize(uAxis);"," float L = max(length(n.xy), 1e-4);"," vec2 ax = n.xy/L;"," vec2 pr = vec2(-ax.y, ax.x);"," float s = dot(p, ax);"," float q = dot(p, pr);"," float Lc = max(L, 0.16);"," float rr = length(p);"," float b2 = clamp(uBeta, 0.0, 0.998);"," float gam = 1.0/sqrt(max(1e-4, 1.0-b2*b2));"," float dA = 1.0/max(1e-3, gam*(1.0-b2*n.z));"," float dB = 1.0/max(1e-3, gam*(1.0+b2*n.z));"," float pA = pow(max(dA,1e-3), 3.4);"," float pB = pow(max(dB,1e-3), 3.4);"," float pm = max(pA, pB);"," float bA = max(pA/pm, 0.035);"," float bB = max(pB/pm, 0.035);"," vec3 col = vec3(0.0);"," for (int j = 0; j < 2; j++) {"," float sg = (j==0) ? 1.0 : -1.0;"," float bo = (j==0) ? bA : bB;"," float df = (j==0) ? dA : dB;"," float sd = s*sg;"," float inj = step(0.0008, sd);"," float d3 = max(sd,0.0)/Lc;"," float emerge = smoothstep(0.03, 0.22, d3);"," float w = 0.0085 + d3*0.058;"," float core = exp(-sq(q/w));"," float sheath = exp(-sq(q/(w*2.6)));"," float cocoon = exp(-sq(q/(w*6.5)));"," float reach = smoothstep(1.28, 0.06, d3);"," float atten = 1.0/(0.55 + d3*1.5);"," float base = reach*atten*inj*emerge;"," float kn = 0.0;"," float rate = min(df, 3.0);"," for (int i = 0; i < 8; i++) {"," float fi = float(i);"," float m = step(fi+0.5, uKnots);"," float ph = fract(t*0.17*rate + fi/max(uKnots,1.0) + sg*0.37 + h11(fi+sg*11.0)*0.22);"," float kp = ph*1.24;"," float kw = 0.022 + kp*0.05;"," kn += m*exp(-sq((d3-kp)/kw))*(1.0-ph*0.72);"," }"," float lobe = exp(-sq((d3-1.1)/0.24))*exp(-sq(q/(w*1.6)))*inj;"," col += uTint*(core*0.6 + kn*core*1.55)*base*bo;"," col += uSheath*(sheath*0.3 + cocoon*0.12)*base*bo*1.4;"," col += uTint*lobe*bo*0.45;"," }"," float eu = dot(p, pr)/0.135;"," float ev = dot(p, ax)/(0.135*max(abs(n.z),0.15));"," float er = sqrt(eu*eu + ev*ev);"," float ring = exp(-sq((er-1.0)*2.6));"," float cphi = (er>1e-4) ? eu/er : 0.0;"," float rb = clamp(0.6/max(0.22, 1.0-0.6*cphi*L), 0.32, 2.2);"," float turb = 0.78 + 0.34*sin(atan(ev, eu+1e-5)*5.0 - t*2.0);"," float torus = (ring*rb*turb + exp(-er*er*2.2)*0.16)*smoothstep(0.030,0.056,rr);"," col += uTorus*torus*0.7;"," col += vec3(1.0,0.94,0.84)*exp(-sq((rr-0.060)*46.0))*0.3;"," col += uSheath*exp(-rr*rr*72.0)*0.32;"," col += vec3(0.8,0.87,1.0)*starfield(p)*0.42;"," col += uSheath*0.035*exp(-rr*1.8) + uBg;"," col = vec3(1.0) - exp(-col*1.15);"," fragColor = vec4(col, 1.0);","}"].join("\n")})}();var D={};function H(t,e){var a=(e=e||{}).position||"top-right",o=D[a];o||((o=B("div","gx-toast-region")).setAttribute("data-pos",a),document.body.appendChild(o),D[a]=o);var r=B("div","gx-toast"+(e.type?" gx-toast--"+e.type:""));r.innerHTML=(e.icon?''+e.icon+"":"")+''+t+"",o.appendChild(r),requestAnimationFrame(function(){r.classList.add("is-open")});var i,n=null==e.duration?3200:e.duration;function l(){r.classList.remove("is-open"),setTimeout(function(){r.parentNode&&r.parentNode.removeChild(r)},320)}return n>0&&(i=setTimeout(l,n)),r.addEventListener("click",function(){clearTimeout(i),l()}),{close:l}}function j(t){t=t||{};var e=B("div","gx-modal-backdrop"),a=B("div","gx-modal"),o=!1!==t.closable,r="";if(o&&(r+=''),t.title&&(r+='

'+t.title+"

"),r+='
'+(t.html||t.body||"")+"
",a.innerHTML=r,t.actions&&t.actions.length){var i=B("div","gx-modal__footer");t.actions.forEach(function(t){var e=B("button","gx-btn "+(t.variant?"gx-btn--"+t.variant:"gx-btn--ghost"),t.label);e.addEventListener("click",function(){t.onClick&&t.onClick(),!1!==t.close&&n()}),i.appendChild(e)}),a.appendChild(i)}function n(){e.classList.remove("is-open"),document.removeEventListener("keydown",l),setTimeout(function(){e.parentNode&&e.parentNode.removeChild(e)},320),t.onClose&&t.onClose()}function l(t){"Escape"===t.key&&o&&n()}if(e.appendChild(a),document.body.appendChild(e),requestAnimationFrame(function(){e.classList.add("is-open")}),o){document.addEventListener("keydown",l),e.addEventListener("click",function(t){t.target===e&&n()});var s=a.querySelector(".gx-modal__close");s&&s.addEventListener("click",n)}return{close:n,el:a}}var U=null;function V(t){function e(){K();var e=B("div","gx-tooltip",t.getAttribute("data-gx-tooltip"));document.body.appendChild(e);var a=t.getBoundingClientRect(),o=e.getBoundingClientRect(),r=t.getAttribute("data-gx-tooltip-pos")||"top",n=a.left+a.width/2-o.width/2,l=a.top-o.height-8;"bottom"===r&&(l=a.bottom+8),"left"===r&&(n=a.left-o.width-8,l=a.top+a.height/2-o.height/2),"right"===r&&(n=a.right+8,l=a.top+a.height/2-o.height/2),e.style.left=i(n,4,window.innerWidth-o.width-4)+"px",e.style.top=Math.max(4,l)+"px",requestAnimationFrame(function(){e.classList.add("is-open")}),U=e}t.addEventListener("mouseenter",e),t.addEventListener("focus",e),t.addEventListener("mouseleave",K),t.addEventListener("blur",K)}function K(){if(U){var t=U;U=null,t.classList.remove("is-open"),setTimeout(function(){t.parentNode&&t.parentNode.removeChild(t)},180)}}function J(t){var e=t.querySelector(".gx-tabs__list");if(e){var a=[].slice.call(t.querySelectorAll(".gx-tab")),o=[].slice.call(t.querySelectorAll(".gx-tab-panel")),r=e.querySelector(".gx-tabs__indicator")||B("div","gx-tabs__indicator");r.parentNode||e.appendChild(r),a.forEach(function(t,e){t.addEventListener("click",function(){n(e)})});var i=a.findIndex(function(t){return"true"===t.getAttribute("aria-selected")});n(i<0?0:i)}function n(t){a.forEach(function(e,a){e.setAttribute("aria-selected",a===t?"true":"false"),o[a]&&(o[a].hidden=a!==t)});var e=a[t];r.style.width=e.offsetWidth+"px",r.style.transform="translateX("+e.offsetLeft+"px)"}}function X(t){var e="multi"!==t.getAttribute("data-gx-accordion"),a=[].slice.call(t.querySelectorAll(".gx-accordion__item"));a.forEach(function(t){var o=t.querySelector(".gx-accordion__header"),r=t.querySelector(".gx-accordion__panel");o&&r&&o.addEventListener("click",function(){var o=t.classList.contains("is-open");e&&a.forEach(function(t){t.classList.remove("is-open");var e=t.querySelector(".gx-accordion__panel");e&&(e.style.maxHeight=null)}),o?(t.classList.remove("is-open"),r.style.maxHeight=null):(t.classList.add("is-open"),r.style.maxHeight=r.scrollHeight+"px")})})}function Y(t){var e=t.querySelector("[data-gx-dropdown-trigger]")||t.querySelector(".gx-btn");e&&(e.addEventListener("click",function(e){e.stopPropagation(),t.classList.toggle("is-open")}),document.addEventListener("click",function(){t.classList.remove("is-open")}))}function Q(t){t.addEventListener("click",function(e){var a=t.getBoundingClientRect(),o=Math.max(a.width,a.height),r=B("span","gx-ripple");r.style.width=r.style.height=o+"px",r.style.left=e.clientX-a.left-o/2+"px",r.style.top=e.clientY-a.top-o/2+"px",t.appendChild(r),setTimeout(function(){r.parentNode&&r.parentNode.removeChild(r)},650)})}function Z(t){t.addEventListener("pointermove",function(e){var a=t.getBoundingClientRect();t.style.setProperty("--gx-mx",e.clientX-a.left+"px"),t.style.setProperty("--gx-my",e.clientY-a.top+"px")})}function $(t){if("undefined"!=typeof IntersectionObserver){var e=new IntersectionObserver(function(t){t.forEach(function(t){if(t.isIntersecting){var a=t.target.getAttribute("data-gx-reveal-delay");a&&(t.target.style.transitionDelay=a+"ms"),t.target.classList.add("is-visible"),e.unobserve(t.target)}})},{threshold:.12});t.forEach(function(t){e.observe(t)})}else t.forEach(function(t){t.classList.add("is-visible")})}function tt(t){var e=[].slice.call(t.querySelectorAll(".gx-segmented__option"));function a(a){e.forEach(function(t,e){t.setAttribute("aria-checked",e===a?"true":"false");var o=t.getAttribute("data-gx-target");if(o){var r=document.querySelector(o);r&&(r.hidden=e!==a)}}),h(t,"gx:change",{index:a,value:e[a].textContent})}e.forEach(function(t,e){t.addEventListener("click",function(){a(e)})});var o=e.findIndex(function(t){return"true"===t.getAttribute("aria-checked")});e.length&&a(o<0?0:o)}function et(t){var e=t.getAttribute("data-gx-popover-target"),a=e?document.querySelector(e):t.nextElementSibling;if(a&&a.classList.contains("gx-popover")){var o=t.parentNode;"static"===window.getComputedStyle(o).position&&(o.style.position="relative"),t.addEventListener("click",function(t){if(t.stopPropagation(),a.classList.toggle("is-open")){var e=document.querySelectorAll(".gx-popover.is-open");[].forEach.call(e,function(t){t!==a&&t.classList.remove("is-open")})}}),document.addEventListener("click",function(e){a.contains(e.target)||e.target===t||a.classList.remove("is-open")})}}function at(t){var e=parseInt(t.getAttribute("data-gx-rating")||"5",10),a=parseInt(t.getAttribute("data-gx-value")||"0",10),o=[];t.innerHTML="";for(var r=1;r<=e;r++){var i=B("span","gx-rating__star","★");i.dataset.v=r,t.appendChild(i),o.push(i)}function n(t){o.forEach(function(e,a){e.classList.toggle("is-active",a"),r+='
'+(t.html||t.body||"")+"
",o.innerHTML=r,t.actions&&t.actions.length){var i=B("div","gx-row");t.actions.forEach(function(t){var e=B("button","gx-btn "+(t.variant?"gx-btn--"+t.variant:"gx-btn--ghost"),t.label);e.addEventListener("click",function(){t.onClick&&t.onClick(),!1!==t.close&&n()}),i.appendChild(e)}),o.appendChild(i)}function n(){a.classList.remove("is-open"),document.removeEventListener("keydown",l),setTimeout(function(){a.parentNode&&a.parentNode.removeChild(a)},320),t.onClose&&t.onClose()}function l(t){"Escape"===t.key&&n()}return a.appendChild(o),document.body.appendChild(a),requestAnimationFrame(function(){a.classList.add("is-open")}),document.addEventListener("keydown",l),a.addEventListener("click",function(t){t.target===a&&n()}),{close:n,el:o}}function nt(e){if(t)if("string"==typeof e)document.documentElement.setAttribute("data-galaxy-theme",e);else if(e&&"object"==typeof e){var a=document.documentElement;Object.keys(e).forEach(function(t){var o=0===t.indexOf("--")?t:"--gx-"+t.replace(/[A-Z]/g,function(t){return"-"+t.toLowerCase()});a.style.setProperty(o,e[t])})}}var lt=[];function st(e){if(t){e=e||document,[].forEach.call(e.querySelectorAll("[data-galaxy]"),function(t){if(!t.__gxMounted){var e=t.getAttribute("data-galaxy");b[e]&&(t.__gxMounted=!0,lt.push(M(e,t,function(t,e){var a={};return[].forEach.call(t.attributes,function(t){if(0===t.name.indexOf(e)&&t.name!==e){var o=t.name.slice(e.length+1).replace(/-([a-z])/g,function(t,e){return e.toUpperCase()});a[o]="true"===(r=t.value)||"false"!==r&&(""===r||isNaN(Number(r))?r.indexOf(",")>=0?r.split(",").map(function(t){return t.trim()}):r:Number(r))}var r}),a}(t,"data-galaxy"))))}}),[].forEach.call(e.querySelectorAll("[data-gx-tooltip]"),V),[].forEach.call(e.querySelectorAll("[data-gx-tabs]"),J),[].forEach.call(e.querySelectorAll("[data-gx-accordion]"),X),[].forEach.call(e.querySelectorAll("[data-gx-dropdown]"),Y),[].forEach.call(e.querySelectorAll("[data-gx-ripple], .gx-btn"),Q),[].forEach.call(e.querySelectorAll(".gx-card--spotlight"),Z),[].forEach.call(e.querySelectorAll("[data-gx-segmented]"),tt),[].forEach.call(e.querySelectorAll("[data-gx-popover]"),et),[].forEach.call(e.querySelectorAll("[data-gx-rating]"),at),[].forEach.call(e.querySelectorAll("[data-gx-copy], [data-gx-copy-target]"),ot),!rt&&t&&(rt=!0,document.addEventListener("click",function(t){var e=t.target.closest&&t.target.closest(".gx-chip__close");if(e){var a=e.closest(".gx-chip");a&&a.parentNode&&a.parentNode.removeChild(a)}}));var a=[].slice.call(e.querySelectorAll(".gx-reveal, [data-gx-reveal]"));a.length&&$(a),[].forEach.call(e.querySelectorAll("[data-gx-modal-target]"),function(t){t.__gxBound||(t.__gxBound=!0,t.addEventListener("click",function(){var e=document.querySelector(t.getAttribute("data-gx-modal-target"));j({title:t.getAttribute("data-gx-modal-title")||"",html:e?e.innerHTML:""})}))}),[].forEach.call(e.querySelectorAll("[data-gx-drawer-target]"),function(t){t.__gxBound||(t.__gxBound=!0,t.addEventListener("click",function(){var e=document.querySelector(t.getAttribute("data-gx-drawer-target"));it({title:t.getAttribute("data-gx-drawer-title")||"",side:t.getAttribute("data-gx-drawer-side")||"right",html:e?e.innerHTML:""})}))})}}t&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",function(){st(document)}):st(document));var ht={version:"3.3.0",create:function(t,e,a){return M(t,e,a)},scrollScene:function(o,r){var i=a(o);if(!i)throw new Error("GalaxyJS: scrollScene target not found");var n=((r=r||{}).scenes||[]).map(function(t){return"string"==typeof t?{type:t}:t});if(!n.length)throw new Error("GalaxyJS: scrollScene needs at least one scene");var l=r.track?a(r.track):i.parentNode||i;i.classList.add("gx-scrollscene");var s=n.map(function(t,e){var a=document.createElement("div");return a.className="gx-scrollscene__layer",a.style.cssText="position:absolute;inset:0;opacity:"+(0===e?1:0)+";transition:opacity .2s linear;display:"+(e<2?"block":"none")+";",i.appendChild(a),{layer:a,ctrl:M(t.type,a,Object.assign({autoplay:!1},t.options||{})),type:t.type}}),h=n.length,c=-1,f=!1,d=!1;function p(){if(!t)return 0;var e=l.getBoundingClientRect(),a=window.innerHeight||document.documentElement.clientHeight||1,o=e.height-a;return o<=0?0:Math.max(0,Math.min(1,-e.top/o))}function u(t){for(var e=t*(h-1),a=Math.min(h-1,Math.floor(e)),o=e-a,i=0;i=.5&&a>16&255,g:e>>8&255,b:255&e}}function s(t,e){return"rgba("+t.r+","+t.g+","+t.b+","+e+")"}function c(t,e,a){try{"function"==typeof CustomEvent&&t.dispatchEvent(new CustomEvent(e,{detail:a}))}catch(t){}}function f(t,e,a){return{r:Math.round(r(t.r,e.r,a)),g:Math.round(r(t.g,e.g,a)),b:Math.round(r(t.b,e.b,a))}}function u(t,e){return(t.colors&&t.colors.length?t.colors:e).map(l)}var h=["float","vec2","vec3","vec4"];function d(t){return"number"==typeof t?1:t.length}function v(t,e,a){var o=t.createShader(e);return o?(t.shaderSource(o,a),t.compileShader(o),t.getShaderParameter(o,t.COMPILE_STATUS)?o:(t.isContextLost()||"undefined"==typeof console||console.error("GalaxyJS: shader compile failed\n"+t.getShaderInfoLog(o)),t.deleteShader(o),null)):null}function p(t,e,a){null!==e&&("number"==typeof a?t.uniform1f(e,a):2===a.length?t.uniform2f(e,a[0],a[1]):3===a.length?t.uniform3f(e,a[0],a[1],a[2]):t.uniform4f(e,a[0],a[1],a[2],a[3]))}function g(t){var e=u(t.opts,["#7c5cff","#22d3ee"]),a=t.opts.background||"#05060f",o=!1;return{draw:function(){if(t.ctx){var r=t.ctx,i=t.width,n=t.height;r.fillStyle=a,r.fillRect(0,0,i,n);var c=r.createRadialGradient(.5*i,.5*n,0,.5*i,.5*n,.7*Math.max(i,n));return c.addColorStop(0,s(e[0],.55)),c.addColorStop(1,s(e[e.length-1],0)),r.fillStyle=c,void r.fillRect(0,0,i,n)}if(!o&&(o=!0,t.el.style.background="radial-gradient(60% 60% at 50% 50%, "+s(e[0],.55)+" 0%, "+s(e[e.length-1],0)+" 100%), "+a,t.gl&&!t.gl.isContextLost())){var f=l(a);t.gl.clearColor(f[0]/255,f[1]/255,f[2]/255,0),t.gl.clear(t.gl.COLOR_BUFFER_BIT)}},destroy:function(){o&&(t.el.style.background="")}}}function m(t,e){k(t,{renderer:"webgl2",defaults:e.defaults||{},fallback:e.fallback||null,setup:function(t){var a=t.gl,o=e.uniforms?e.uniforms(t.opts,t):{},r=function(t,e,a){var o="";for(var r in a)Object.prototype.hasOwnProperty.call(a,r)&&(o+="uniform "+h[d(a[r])-1]+" "+r+";\n");var i="#version 300 es\nprecision highp float;\nuniform vec2 uResolution;\nuniform float uTime;\nuniform vec3 uMouse;\n"+o+"out vec4 fragColor;\n"+e,n=v(t,t.VERTEX_SHADER,"#version 300 es\nvoid main(){vec2 p=vec2(float((gl_VertexID<<1)&2),float(gl_VertexID&2));gl_Position=vec4(p*2.0-1.0,0.0,1.0);}"),l=v(t,t.FRAGMENT_SHADER,i);if(!n||!l)return null;var s=t.createProgram();return t.attachShader(s,n),t.attachShader(s,l),t.linkProgram(s),t.deleteShader(n),t.deleteShader(l),t.getProgramParameter(s,t.LINK_STATUS)?s:(t.isContextLost()||"undefined"==typeof console||console.error("GalaxyJS: shader link failed\n"+t.getProgramInfoLog(s)),t.deleteProgram(s),null)}(a,e.fragment,o);if(!r)return g(t);var i={};function n(t){return t in i||(i[t]=a.getUniformLocation(r,t)),i[t]}return a.useProgram(r),{draw:function(o){if(!a.isContextLost()){a.useProgram(r),a.uniform2f(n("uResolution"),t.canvas.width,t.canvas.height),a.uniform1f(n("uTime"),void 0!==e.staticTime&&t.reduced?e.staticTime:o),a.uniform3f(n("uMouse"),t.mouse.x*t.dpr,(t.height-t.mouse.y)*t.dpr,t.mouse.active?1:0);var i=e.uniforms?e.uniforms(t.opts,t):{};for(var l in i)Object.prototype.hasOwnProperty.call(i,l)&&p(a,n(l),i[l]);a.drawArrays(a.TRIANGLES,0,3)}},destroy:function(){r&&!a.isContextLost()&&a.deleteProgram(r)}}}})}var x=null,b=null;function y(t,e){k(t,{renderer:"three",defaults:e.defaults||{},fallback:e.fallback||null,setup:function(a){var o,r=(e.fallback||g)(a),i=null,n=!1,l={w:a.width,h:a.height};return(o=a.opts.threeUrl,x?Promise.resolve(x):b||(b=Promise.resolve().then(function(){return import(o||"https://cdn.jsdelivr.net/npm/three@0.185.1/build/three.module.min.js")}).then(function(t){return x=t,t}).catch(function(t){throw b=null,t}))).then(function(t){n||(i=e.scene(t,a),r.destroy&&r.destroy(),i.resize&&i.resize(l.w,l.h),a.reduced&&i.draw(void 0!==e.staticTime?e.staticTime:0,0))}).catch(function(e){i=null,"undefined"!=typeof console&&console.warn('GalaxyJS: "'+t+'" needs three.js; showing the still fallback.',e&&e.message?e.message:e)}),{draw:function(t,e){a.gl&&a.gl.isContextLost()||(i||r).draw(t,e)},resize:function(t,e){l.w=t,l.h=e;var a=i||r;a.resize&&a.resize(t,e)},update:function(t){i&&i.update&&i.update(t)},destroy:function(){n=!0,i&&i.destroy?i.destroy():r.destroy&&r.destroy(),i=null}}}})}function M(t,e){var a=new t.WebGLRenderer({canvas:e.canvas,context:e.gl,antialias:!1,alpha:!0,premultipliedAlpha:!0});return a.setPixelRatio(e.dpr),a.setSize(e.width,e.height,!1),"outputColorSpace"in a&&(a.outputColorSpace=t.SRGBColorSpace),a.toneMapping=t.ACESFilmicToneMapping,a.toneMappingExposure=1,a}function w(t,e){t&&t.traverse&&t.traverse(function(t){t.geometry&&t.geometry.dispose&&t.geometry.dispose();var e=t.material;e&&(Array.isArray(e)?e:[e]).forEach(function(t){for(var e in t)t[e]&&t[e].isTexture&&t[e].dispose&&t[e].dispose();t.dispose&&t.dispose()})}),(e||[]).forEach(function(t){t&&t.dispose&&t.dispose()})}var S=function(){var e=[],a=null;function o(t){for(var r=0;r=0&&e.splice(o,1),e.length||null===a||(cancelAnimationFrame(a),a=null)}}}(),C={};function k(t,e){C[t]={setup:e.setup,defaults:e.defaults||{},renderer:e.renderer||"2d",fallback:e.fallback||null}}function T(o,r,i){var n=a(r);if(!n)throw new Error('GalaxyJS: target not found for "'+o+'"');var l=C[o];if(!l)throw new Error('GalaxyJS: unknown animation "'+o+'"');n.classList.add("gx-surface-host");var s=document.createElement("canvas");s.className="gx-canvas",n.appendChild(s);var c=Object.assign({},l.defaults,i||{}),f=null,u=null,h=l.setup;if("webgl2"===l.renderer||"three"===l.renderer){try{f=s.getContext("webgl2",{alpha:!0,antialias:!1,premultipliedAlpha:!0,powerPreference:"low-power"})}catch(t){f=null}f||(h=l.fallback||g)}f||(u=s.getContext("2d"));var d={el:n,canvas:s,ctx:u,gl:f,opts:c,width:1,height:1,dpr:1,mouse:{x:-9999,y:-9999,active:!1},reduced:e,t:0},v=h(d);function p(){var t=n.getBoundingClientRect();d.width=Math.max(1,Math.round(t.width)),d.height=Math.max(1,Math.round(t.height)),d.dpr=Math.min(window.devicePixelRatio||1,2),s.width=Math.round(d.width*d.dpr),s.height=Math.round(d.height*d.dpr),s.style.width=d.width+"px",s.style.height=d.height+"px",f?f.viewport(0,0,s.width,s.height):u.setTransform(d.dpr,0,0,d.dpr,0,0),v.resize&&v.resize(d.width,d.height)}function m(t){var e=n.getBoundingClientRect(),a=t.touches?t.touches[0]:t;d.mouse.x=a.clientX-e.left,d.mouse.y=a.clientY-e.top,d.mouse.active=!0}function x(){d.mouse.active=!1,d.mouse.x=-9999,d.mouse.y=-9999}c.interactive&&(s.setAttribute("data-interactive","true"),n.addEventListener("pointermove",m),n.addEventListener("pointerleave",x)),p();var b="undefined"!=typeof performance?performance.now():Date.now(),y={running:!1,fn:function(t){var e=Math.min(.05,(t-b)/1e3);b=t,d.t+=e,v.draw(d.t,e)}},M=null,w=null,k=!1;function T(){return e?(v.draw(0,0),R):(y.running||(y.running=!0,b="undefined"!=typeof performance?performance.now():Date.now(),S.add(y)),R)}function P(){return y.running=!1,S.remove(y),R}"undefined"!=typeof ResizeObserver?(M=new ResizeObserver(p)).observe(n):t&&window.addEventListener("resize",p),"undefined"==typeof IntersectionObserver||e||(w=new IntersectionObserver(function(t){var e=t[0].isIntersecting;e&&!k?T():(k=!1,e||P())},{threshold:.01})).observe(n);var R={el:n,canvas:s,type:o,start:T,stop:P,pause:function(){return P(),k=!0,R},resume:function(){return k=!1,T(),R},update:function(t){return Object.assign(d.opts,t||{}),v.update?v.update(d.opts):v.resize&&v.resize(d.width,d.height),e&&v.draw(d.t,0),R},options:function(){return d.opts},destroy:function(){P(),M&&M.disconnect(),w&&w.disconnect(),c.interactive&&(n.removeEventListener("pointermove",m),n.removeEventListener("pointerleave",x)),v.destroy&&v.destroy(),s.parentNode&&s.parentNode.removeChild(s),n.classList.remove("gx-surface-host")}};return!1!==c.autoplay&&T(),R}function P(t,e){var a=t.ctx;!1===t.opts.trail||e<=0?a.clearRect(0,0,t.width,t.height):(a.fillStyle=s(l(t.opts.background||"#05060f"),e),a.fillRect(0,0,t.width,t.height))}function R(t){var e=t.ctx;e.clearRect(0,0,t.width,t.height),e.fillStyle=s(l(t.opts.background||"#05060f"),1),e.fillRect(0,0,t.width,t.height)}function z(t){var e=l(t);return[e.r/255,e.g/255,e.b/255]}function L(t,e,a){var o=u(t,e),r=o[Math.min(a,o.length-1)];return[r.r/255,r.g/255,r.b/255]}function A(t,e){var a=i(e,0,1)*(t.length-1),o=Math.min(t.length-2,Math.floor(a));return f(t[o],t[o+1],a-o)}function E(t){var e=l(t);return[e.r/255,e.g/255,e.b/255]}function F(t,e,a){var o=t&&t.colors&&t.colors.length?t.colors:null;return E(o&&o[e]||a)}function q(t,e,a,o){return i("number"==typeof t&&isFinite(t)?t:e,a,o)}function D(t,e){var a,o,r,n=i(t,1200,2e4)/100;n<=66?(a=255,o=99.4708025861*Math.log(n)-161.1195681661,r=n<=19?0:138.5177312231*Math.log(n-10)-305.0447927307):(a=329.698727446*Math.pow(n-60,-.1332047592),o=288.1221695283*Math.pow(n-60,-.0755148492),r=255);var l=void 0===e?1:e;return[i(a,0,255)/255*l,i(o,0,255)/255*l,i(r,0,255)/255*l]}k("starfield",{defaults:{count:160,speed:1,colors:["#ffffff","#bcd4ff","#fff1c4"],background:"#05060f",trail:!1},setup:function(t){var e=[];function a(){e=[];for(var a=t.width*t.height,r=Math.max(40,Math.round(t.opts.count*a/921600)),i=u(t.opts,["#ffffff"]),l=0;lt.height+2&&(l.y=-2,l.x=Math.random()*t.width);var c=.5+.5*Math.sin(a*(1+l.z)+l.tw);r.beginPath(),r.fillStyle=s(l.col,.25+.6*c),r.arc(l.x,l.y,l.r*(.6+l.z),0,n()),r.fill()}}}}}),k("warp",{defaults:{count:220,speed:1,colors:["#ffffff","#9bd0ff","#c9b8ff"],background:"#03040d",trail:!0},setup:function(t){var e,a=[],l=0,c=0;function f(){return{a:o(0,n()),d:o(0,1),len:0,col:e[Math.random()*e.length|0]}}function h(){e=u(t.opts,["#ffffff"]),l=t.width/2,c=t.height/2,a=[];for(var o=0;o1&&(v.d=o(0,.15),v.a=o(0,n()),p=v.d);var g=p*h,m=v.d*h,x=l+Math.cos(v.a)*g,b=c+Math.sin(v.a)*g,y=l+Math.cos(v.a)*m,M=c+Math.sin(v.a)*m;u.strokeStyle=s(v.col,i(v.d,.1,1)),u.lineWidth=r(.4,2.4,v.d),u.beginPath(),u.moveTo(x,b),u.lineTo(y,M),u.stroke()}}}}}),k("blackHole",{defaults:{radius:.18,speed:1,colors:["#ff7b00","#ffd166","#7c5cff"],background:"#04040c",particles:220},setup:function(t){var e,a,r,i,l=[];function c(){i=u(t.opts,["#ff7b00","#ffd166"]),e=t.width/2,a=t.height/2,r=Math.min(t.width,t.height)*t.opts.radius,l=[];for(var s=0;s1.2)&&(f.vx*=-1),(f.y<-.2||f.y>1.2)&&(f.vy*=-1);var u=f.x*t.width,h=f.y*t.height,d=f.r*i*(.9+.1*Math.sin(.6*e+c)),v=r.createRadialGradient(u,h,0,u,h,d);v.addColorStop(0,s(f.col,.5)),v.addColorStop(.5,s(f.col,.16)),v.addColorStop(1,"rgba(0,0,0,0)"),r.fillStyle=v,r.beginPath(),r.arc(u,h,d,0,n()),r.fill()}r.globalCompositeOperation="source-over"}}}}),k("spiral",{defaults:{stars:600,arms:3,speed:1,colors:["#ffffff","#9bd0ff","#c9b8ff","#ffd6a5"],background:"#04040c"},setup:function(t){var e,a,r,i=[];function l(){r=u(t.opts,["#ffffff","#9bd0ff"]),e=t.width/2,a=t.height/2,i=[];for(var l=.46*Math.min(t.width,t.height),s=0;st.width+50||v.y>t.height+50)a[d]=i();else{var g=v.x-u*v.len,m=v.y-h*v.len,x=l.createLinearGradient(v.x,v.y,g,m);x.addColorStop(0,s(v.col,.9)),x.addColorStop(1,"rgba(0,0,0,0)"),l.strokeStyle=x,l.lineWidth=2,l.lineCap="round",l.beginPath(),l.moveTo(v.x,v.y),l.lineTo(g,m),l.stroke()}}}}}}),k("constellation",{defaults:{count:90,speed:1,link:130,colors:["#7c5cff","#22d3ee"],background:"#05060f",interactive:!0},setup:function(t){var e,a=[];function r(){e=u(t.opts,["#7c5cff","#22d3ee"]);var r=t.width*t.height,i=Math.max(24,Math.round(t.opts.count*r/921600));a=[];for(var n=0;nt.width)&&(u.vx*=-1),(u.y<0||u.y>t.height)&&(u.vy*=-1),t.mouse.active){var h=u.x-t.mouse.x,d=u.y-t.mouse.y,v=Math.hypot(h,d);v<140&&v>.1&&(u.x+=h/v*.8,u.y+=d/v*.8)}}for(var p=0;p.1){var v=(120-d)/120*l*1.4;f.vx+=u/d*v,f.vy+=h/d*v}}f.vx*=.96,f.vy*=.96,f.x+=(f.vx+o(-.05,.05))*t.opts.speed,f.y+=(f.vy+o(-.05,.05))*t.opts.speed,f.x<0&&(f.x=t.width),f.x>t.width&&(f.x=0),f.y<0&&(f.y=t.height),f.y>t.height&&(f.y=0),i.fillStyle=s(f.col,.85),i.beginPath(),i.arc(f.x,f.y,f.r,0,n()),i.fill()}}}}}),k("aurora",{defaults:{bands:4,speed:1,colors:["#22d3ee","#7c5cff","#34d399","#f472b6"],background:"#04060f"},setup:function(t){var e;function a(){e=u(t.opts,["#22d3ee","#7c5cff"])}return a(),{resize:a,draw:function(a,o){var r=t.ctx;r.clearRect(0,0,t.width,t.height),r.fillStyle=s(l(t.opts.background),1),r.fillRect(0,0,t.width,t.height),r.globalCompositeOperation="lighter";for(var i=0;i.1&&(c.x+=f/h*1.2,c.y+=u/h*1.2)}c.x<0&&(c.x=t.width),c.x>t.width&&(c.x=0),c.y<0&&(c.y=t.height),c.y>t.height&&(c.y=0);var d=.25+.75*(.5+.5*Math.sin(2.2*a+c.ph)),v=5*c.r,p=i.createRadialGradient(c.x,c.y,0,c.x,c.y,v);p.addColorStop(0,s(c.col,.9*d)),p.addColorStop(1,s(c.col,0)),i.fillStyle=p,i.beginPath(),i.arc(c.x,c.y,v,0,n()),i.fill(),i.fillStyle=s(c.col,d),i.beginPath(),i.arc(c.x,c.y,.5*c.r,0,n()),i.fill()}i.globalCompositeOperation="source-over"}}}}),k("matrix",{defaults:{speed:1,colors:["#34d399"],background:"#020509",font:16,glyphs:"01"},setup:function(t){var e,a,r,i,n;function c(){n=l(t.opts.colors&&t.opts.colors[0]||"#34d399"),a=t.opts.font,i=t.opts.glyphs||"01アカサタナハマヤラ0123456789",e=Math.ceil(t.width/a),r=[];for(var s=0;st.height&&Math.random()>.975&&(d.y=o(-200,0))}}}}}),k("plasma",{defaults:{speed:1,scale:1,colors:["#7c5cff","#22d3ee","#f472b6"]},setup:function(t){var e,a;function o(){e=u(t.opts,["#7c5cff","#22d3ee","#f472b6"]),a=Math.max(10,22/(t.opts.scale||1))}function r(t){var a=e.length,o=(t%1+1)%1,r=Math.floor(o*a),i=(r+1)%a;return f(e[r],e[i],o*a-r)}return o(),{resize:o,draw:function(e,o){for(var i=t.ctx,n=e*t.opts.speed,l=0;l=0;h--){var d=e[h];d.y+=d.vy*t.opts.speed,d.vy+=.06,u.fillStyle=s(d.col,1),u.beginPath(),u.arc(d.x,d.y,2,0,n()),u.fill(),(d.y<=d.target||d.vy>=0)&&(c(d.x,d.y,d.col),e.splice(h,1))}for(var v=a.length-1;v>=0;v--){var p=a[v];p.vx*=.985,p.vy=.985*p.vy+t.opts.gravity,p.x+=p.vx*t.opts.speed,p.y+=p.vy*t.opts.speed,p.life-=.55*f,p.life<=0?a.splice(v,1):(u.fillStyle=s(p.col,p.life),u.beginPath(),u.arc(p.x,p.y,1.8*p.life+.4,0,n()),u.fill())}u.globalCompositeOperation="source-over"}}}}),k("snow",{defaults:{count:200,speed:1,wind:.4,colors:["#ffffff","#dbeafe"],background:"#0a0f1f"},setup:function(t){var e,a;function r(){a=u(t.opts,["#ffffff"]);var r=Math.max(40,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var i=0;it.height+4&&(l.y=-4,l.x=Math.random()*t.width),l.x>t.width+4&&(l.x=-4),l.x<-4&&(l.x=t.width+4),r.fillStyle=s(l.col,.85),r.beginPath(),r.arc(l.x,l.y,l.r,0,n()),r.fill()}}}}}),k("waves",{defaults:{layers:4,speed:1,amplitude:26,colors:["#0ea5e9","#22d3ee","#7c5cff","#1e3a8a"],background:"#040814"},setup:function(t){var e;function a(){e=u(t.opts,["#0ea5e9","#22d3ee"])}return a(),{resize:a,draw:function(a,o){R(t);for(var r=t.ctx,i=0;ir+o(60,160))break}}(o(.2*t.width,.8*t.width),0,!1),a=c,r=1,i=o(.4,1.6)/t.opts.speed),a)if((r-=2.4*l)<=0)a=null;else{var u=Math.min(.12,.12*r);f.fillStyle=s(e[0],u),f.fillRect(0,0,t.width,t.height),f.globalCompositeOperation="lighter",f.lineCap="round";for(var h=0;h=0;u--){var h=e[u];h.r+=70*t.opts.speed*i;var d=1-h.r/h.max;d<=0?e.splice(u,1):(f.strokeStyle=s(h.col,.8*d),f.lineWidth=2*d+.4,f.beginPath(),f.arc(h.x,h.y,h.r,0,n()),f.stroke())}}}}}),k("comets",{defaults:{count:4,speed:1,colors:["#ffffff","#a5c8ff","#c9b8ff","#ffd6a5"],background:"#04050d",stars:!0},setup:function(t){var e,a,r;function i(){var e=Math.random()<.5,a=o(1.2,2.4);return{x:e?-40:t.width+40,y:o(0,.7*t.height),vx:(e?1:-1)*a,vy:o(.2,.8)*a,hist:[],size:o(2.5,5),col:r[Math.random()*r.length|0]}}function l(){r=u(t.opts,["#ffffff","#a5c8ff"]),e=[];for(var n=0;n26&&u.hist.shift(),u.x<-80||u.x>t.width+80||u.y>t.height+80)e[f]=i();else{for(var h=0;hn&&e.length<460;)e.push(l()),r-=n;for(var c=e.length-1;c>=0;c--){var f=e[c];f.vy+=t.opts.gravity*o*6,f.y+=f.vy*t.opts.speed,f.x+=(f.vx+.7*Math.sin(2*a+f.sway))*t.opts.speed,f.rot+=f.vr,f.y>t.height+20?e.splice(c,1):(i.save(),i.translate(f.x,f.y),i.rotate(f.rot),i.fillStyle=s(f.col,1),i.fillRect(-f.w/2,-f.hh/2,f.w,f.hh*(.55+.45*Math.abs(Math.cos(f.rot)))),i.restore())}}}}}),k("bubbles",{defaults:{count:42,speed:1,colors:["#a8e6ff","#7c5cff","#34d399"],background:"#04121e"},setup:function(t){var e,a;function r(e){return{x:o(0,t.width),y:e?o(0,t.height):t.height+20,r:o(5,22),sp:o(.3,1),wob:o(0,n()),col:a[Math.random()*a.length|0]}}function i(){a=u(t.opts,["#a8e6ff"]);var o=Math.max(14,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var i=0;i1.3&&(c.x=-.3),c.x<-.3&&(c.x=1.3);var f=c.x*t.width,u=(c.y+.03*Math.sin(.3*a+l))*t.height,h=c.r*i*.6,d=r.createRadialGradient(f,u,0,f,u,h);d.addColorStop(0,s(c.col,.16)),d.addColorStop(1,s(c.col,0)),r.fillStyle=d,r.beginPath(),r.arc(f,u,h,0,n()),r.fill()}}}}}),k("grid",{defaults:{speed:1,lines:16,colors:["#f472b6","#7c5cff"],background:"#0a0114"},setup:function(t){var e,a,o;function r(){e=u(t.opts,["#f472b6","#7c5cff"]),a=t.width/2,o=.42*t.height}return r(),{resize:r,draw:function(r,i){R(t);var n=t.ctx,l=n.createRadialGradient(a,o,0,a,o,.4*t.height);l.addColorStop(0,s(e[0],.35)),l.addColorStop(1,s(e[0],0)),n.fillStyle=l,n.fillRect(0,0,t.width,o+20),n.shadowColor=s(e[1]||e[0],.8),n.shadowBlur=8,n.strokeStyle=s(e[1]||e[0],.8),n.lineWidth=1.4;for(var c=t.opts.lines,f=r*t.opts.speed*.35%1,u=0;ut.height+10&&(h.y=-10,h.x=o(-.1,1.1)*t.width),n.strokeStyle=s(h.col,.5),n.beginPath(),n.moveTo(h.x,h.y),n.lineTo(h.x-c*h.len,h.y-f*h.len),n.stroke()}}}}}),k("vortex",{defaults:{count:240,speed:1,colors:["#7c5cff","#22d3ee","#f472b6"],background:"#04040c"},setup:function(t){var e,a,r,i,l;function c(t){return{a:o(0,n()),r:t?Math.random():1,sp:o(.4,1),col:l[Math.random()*l.length|0]}}function f(){l=u(t.opts,["#7c5cff","#22d3ee"]),a=t.width/2,r=t.height/2,i=Math.hypot(a,r);var o=Math.max(60,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var n=0;n=1)e[l]=r(),e[l].life=0;else{var c=Math.sin(s.life*Math.PI);n(i,s.x,s.y,s.size*c,s.col,c)}}i.globalCompositeOperation="source-over"}}}}),k("tunnel",{defaults:{rings:20,sides:6,speed:1,colors:["#22d3ee","#f472b6","#7c5cff"],background:"#03030b"},setup:function(t){var e,a,o;function i(){e=t.width/2,a=t.height/2,o=u(t.opts,["#22d3ee","#f472b6"])}function l(t,o,r,i){t.beginPath();for(var l=0;l<=i;l++){var s=r+l/i*n(),c=e+Math.cos(s)*o,f=a+Math.sin(s)*o*.78;0===l?t.moveTo(c,f):t.lineTo(c,f)}}return i(),{resize:i,draw:function(i,n){P(t,.28);for(var c=t.ctx,u=Math.hypot(e,a),h=0;h0&&(l+=e[p].vx,c+=e[p].vy,f+=e[p].x,u+=e[p].y,x<380&&(h-=g,d-=m),v++)}if(v&&(n.vx+=.04*(l/v-n.vx)+8e-4*(f/v-n.x)+.02*h,n.vy+=.04*(c/v-n.vy)+8e-4*(u/v-n.y)+.02*d),t.mouse.active){var b=t.mouse.x-n.x,y=t.mouse.y-n.y;n.vx+=.0012*b,n.vy+=.0012*y}var M=Math.hypot(n.vx,n.vy);M>2.4&&(n.vx=n.vx/M*2.4,n.vy=n.vy/M*2.4),n.x+=n.vx*t.opts.speed*1.6,n.y+=n.vy*t.opts.speed*1.6,n.x<0&&(n.x=t.width),n.x>t.width&&(n.x=0),n.y<0&&(n.y=t.height),n.y>t.height&&(n.y=0);var w=Math.atan2(n.vy,n.vx);r.fillStyle=s(n.col,.9),r.save(),r.translate(n.x,n.y),r.rotate(w),r.beginPath(),r.moveTo(5,0),r.lineTo(-4,3),r.lineTo(-4,-3),r.closePath(),r.fill(),r.restore()}}}}}),k("ribbons",{defaults:{count:5,speed:1,colors:["#7c5cff","#22d3ee","#f472b6"],background:"#05060f"},setup:function(t){var e,a;function r(){a=u(t.opts,["#7c5cff","#22d3ee"]),e=[];for(var r=0;rt.width||c.y<0||c.y>t.height)&&(e[l]=r())}}}}}),k("globe",{defaults:{count:320,speed:1,colors:["#22d3ee","#7c5cff"],background:"#05060f"},setup:function(t){var e,a,o,r,i;function l(){i=u(t.opts,["#22d3ee","#7c5cff"]),a=t.width/2,o=t.height/2,r=.4*Math.min(t.width,t.height);var n=t.opts.count;e=[];for(var l=Math.PI*(3-Math.sqrt(5)),s=0;st.width&&(e-=t.width,v=0,f.fillStyle=s(l(t.opts.background),1),f.fillRect(0,0,t.width,t.height)),f.fillStyle=s(l(t.opts.background),1),f.fillRect(e,0,26,t.height),f.strokeStyle=s(i,1),f.lineWidth=1,f.beginPath(),f.moveTo(v,u),f.lineTo(e,u),f.stroke();var p,g,m=u-(p=e%o/o,g=0,g+=.12*Math.exp(-Math.pow((p-.15)/.02,2)),g-=.16*Math.exp(-Math.pow((p-.27)/.008,2)),g+=1*Math.exp(-Math.pow((p-.3)/.007,2)),((g-=.38*Math.exp(-Math.pow((p-.34)/.008,2)))+.22*Math.exp(-Math.pow((p-.52)/.03,2)))*h);f.strokeStyle=s(r,1),f.lineWidth=2,f.shadowColor=s(r,.9),f.shadowBlur=8,f.beginPath(),f.moveTo(v,a),f.lineTo(e,m),f.stroke(),f.shadowBlur=0,a=m}}}}),k("equalizer",{defaults:{bars:32,speed:1,colors:["#7c5cff","#22d3ee","#34d399"],background:"#05060f"},setup:function(t){var e,a;function r(){a=u(t.opts,["#7c5cff","#22d3ee"]),e=[];for(var r=0;rMath.PI-.12&&(m.seen=1),m.seen=Math.max(0,m.seen-.4*c),m.seen>.01&&(f.fillStyle=s(h,m.seen),f.beginPath(),f.arc(e+Math.cos(m.a)*m.r,a+Math.sin(m.a)*m.r,3+2*m.seen,0,n()),f.fill())}}}}}),k("embers",{defaults:{count:120,speed:1,colors:["#fbbf24","#fb7185","#f59e0b"],background:"#0a0402"},setup:function(t){var e,a;function r(e){return{x:o(.2*t.width,.8*t.width),y:e?o(0,t.height):t.height+6,vy:o(.4,1.2),drift:o(0,n()),r:o(1,3),life:o(.5,1),col:a[Math.random()*a.length|0]}}function i(){a=u(t.opts,["#fbbf24","#fb7185"]);var o=Math.max(40,Math.round(t.opts.count*t.width*t.height/921600));e=[];for(var i=0;i=v.length&&(r="hold",i=1.4)):"hold"===r?(r="erase",i=.5):(o--,i=.03/t.opts.speed,o<=0&&(r="type",a=(a+1)%e.length,i=.3)));var p=v.slice(0,Math.max(0,o));d.font="700 "+u+"px "+("string"==typeof t.opts.fontFamily?t.opts.fontFamily:"Inter, system-ui, sans-serif"),d.textBaseline="middle";var g=d.measureText(p).width,m=t.width/2-g/2,x=t.height/2;d.fillStyle=s(c,1),d.textAlign="left",d.fillText(p,m,x),n%1<.5&&(d.fillStyle=s(f,1),d.fillRect(m+g+3,x-.45*u,.12*u,.9*u))}}}}),k("spirograph",{defaults:{speed:1,colors:["#7c5cff","#22d3ee","#f472b6"],background:"#05060f"},setup:function(t){var e,a,r,i,l,c,h,d,v,p,g;function m(){h=o(.6,1),d=o(.12,.42),v=o(.3,.9),i=0,l=null,g=o(8,16)}function x(){e=t.width/2,a=t.height/2,r=.32*Math.min(t.width,t.height),c=u(t.opts,["#7c5cff","#22d3ee","#f472b6"]),p=0,m()}function b(t){var o=(h-d)/d;return{x:e+((h-d)*Math.cos(t)+v*d*Math.cos(o*t))*r,y:a+((h-d)*Math.sin(t)-v*d*Math.sin(o*t))*r}}return x(),{resize:x,draw:function(e,a){P(t,.035);for(var o=t.ctx,r=0;r<7;r++){i+=.06*t.opts.speed,p+=.0016;var u=b(i);if(l){var h=f(c[Math.floor(p)%c.length],c[(Math.floor(p)+1)%c.length],p%1);o.strokeStyle=s(h,.8),o.lineWidth=1.4,o.beginPath(),o.moveTo(l.x,l.y),o.lineTo(u.x,u.y),o.stroke()}l=u,i>g*n()&&m()}}}}}),k("supernova",{defaults:{speed:1,colors:["#fff1c4","#ff9d5c","#7c5cff","#22d3ee"],background:"#04040c",particles:150},setup:function(t){var e,a,l,c,f,h,d,v;function p(){e=t.width/2,a=t.height/2,l=u(t.opts,["#fff1c4","#ff9d5c","#7c5cff"]),c=[],f="charge",h=0,d=0,v=.25}return p(),{resize:p,draw:function(u,p){P(t,.16);var g=t.ctx;g.globalCompositeOperation="lighter";var m=p*t.opts.speed;if("charge"===f){v=Math.min(1.7,v+.7*m),h+=m;var x=.045*Math.min(t.width,t.height)*v,b=.7+.3*Math.sin(22*u)*(v/1.7),y=g.createRadialGradient(e,a,0,e,a,6*x);y.addColorStop(0,s(l[0],.9*b)),y.addColorStop(.4,s(l[0],.22)),y.addColorStop(1,"rgba(0,0,0,0)"),g.fillStyle=y,g.beginPath(),g.arc(e,a,6*x,0,n()),g.fill(),h>2.3&&(f="burst",d=0,function(){c=[];for(var e=t.opts.particles,a=Math.min(t.width,t.height),r=0;r1.05&&(f="charge",h=0,v=.25)}g.globalCompositeOperation="source-over"}}}}),k("quasar",{defaults:{speed:1,colors:["#22d3ee","#7c5cff","#eef3ff"],background:"#04040c",particles:170},setup:function(t){var e,a,i,l,c,f;function h(){e=t.width/2,a=t.height/2,i=.06*Math.min(t.width,t.height),l=u(t.opts,["#22d3ee","#7c5cff","#eef3ff"]),c=[];for(var r=0;r1&&(M.p-=1);var w=a+M.dir*M.p*v,S=M.p*i*2.2,C=e+M.off*S,k=.8*(1-M.p);h.fillStyle=s(M.col,k),h.beginPath(),h.arc(C,w,r(2.2,.4,M.p),0,n()),h.fill()}var T=h.createRadialGradient(e,a,0,e,a,3*i);T.addColorStop(0,s(l[2]||l[0],.95)),T.addColorStop(.5,s(l[0],.3)),T.addColorStop(1,"rgba(0,0,0,0)"),h.fillStyle=T,h.beginPath(),h.arc(e,a,3*i,0,n()),h.fill(),h.globalCompositeOperation="source-over",h.fillStyle="#000",h.beginPath(),h.arc(e,a,.8*i,0,n()),h.fill()}}}}),k("starcluster",{defaults:{speed:1,count:280,colors:["#ffffff","#bcd4ff","#fff1c4","#ffd6a5"],background:"#04040c"},setup:function(t){var e,a,r,i,l,c;function f(){a=t.width/2,r=t.height/2,i=.34*Math.min(t.width,t.height),l=u(t.opts,["#ffffff","#bcd4ff"]),c=0,e=[];for(var s=t.opts.count,f=0;ft.width)&&(u.vx*=-1),(u.y<0||u.y>t.height)&&(u.vy*=-1)}i.globalCompositeOperation="lighter";for(var h=0;ht.width+2&&(u.x=-2),u.x<-2&&(u.x=t.width+2),u.y>t.height+2&&(u.y=-2),u.y<-2&&(u.y=t.height+2);var d=.4+.6*Math.sin(1.5*a+u.ph);l.fillStyle=s(u.col,(.1+.5*u.z)*d),l.beginPath(),l.arc(u.x,u.y,u.r*(.5+u.z),0,n()),l.fill()}l.globalCompositeOperation="source-over"}}}}),k("orrery",{defaults:{speed:1,rings:5,colors:["#c9a959","#22d3ee","#7c5cff","#eef3ff"],background:"#05060f"},setup:function(t){var e,a,r,i,l;function c(){e=t.width/2,a=t.height/2,r=.42*Math.min(t.width,t.height),i=u(t.opts,["#c9a959","#22d3ee"]),l=[];for(var s=t.opts.rings,c=0;c.7&&(r.strokeStyle=s(l.col,.12*u),r.lineWidth=1,r.beginPath(),r.arc(l.x,l.y,.9*c,0,n()),r.stroke())}r.globalCompositeOperation="source-over"}}}}),k("magnetosphere",{defaults:{speed:1,colors:["#22d3ee","#7c5cff","#a8d8ff"],background:"#04040c",lines:7},setup:function(t){var e,a,r,i,l;function c(){e=t.width/2,a=t.height/2,r=.12*Math.min(t.width,t.height),i=u(t.opts,["#22d3ee","#7c5cff"]),l=[];for(var n=0;n<72;n++)l.push({shell:1+(Math.random()*t.opts.lines|0),p:Math.random(),side:Math.random()<.5?1:-1,v:o(.3,.8),col:i[Math.random()*i.length|0]})}function f(t,o,i){var n=o*Math.PI,l=r*t*Math.sin(n)*Math.sin(n);return{x:e+i*l*Math.sin(n),y:a-l*Math.cos(n)}}return c(),{resize:c,draw:function(o,c){R(t);var u=t.ctx;u.globalCompositeOperation="lighter";var h=u.createRadialGradient(e,a,0,e,a,1.4*r);h.addColorStop(0,s(i[0],.5)),h.addColorStop(.6,s(i[1]||i[0],.18)),h.addColorStop(1,"rgba(0,0,0,0)"),u.fillStyle=h,u.beginPath(),u.arc(e,a,1.4*r,0,n()),u.fill(),u.fillStyle="#0a0e18",u.beginPath(),u.arc(e,a,r,0,n()),u.fill();for(var d=t.opts.lines,v=-1;v<=1;v+=2)for(var p=1;p<=d;p++){var g=1.4+.7*p;u.strokeStyle=s(i[1]||i[0],.12+.04*p),u.lineWidth=1,u.beginPath();for(var m=0;m<=40;m++){var x=f(g,m/40,v);0===m?u.moveTo(x.x,x.y):u.lineTo(x.x,x.y)}u.stroke()}for(var b=0;b1&&(y.p-=1);var M=f(1.4+.7*y.shell,y.p,y.side);u.fillStyle=s(y.col,.7*(.4+.6*Math.sin(y.p*Math.PI))),u.beginPath(),u.arc(M.x,M.y,1.6,0,n()),u.fill()}u.globalCompositeOperation="source-over"}}}}),m("lensing",{defaults:{mass:1,spin:.35,stars:1,speed:1,interactive:!0,colors:["#ffd9a0","#9fd0ff"],background:"#05060f"},staticTime:11,uniforms:function(t){return{uMass:i(t.mass,.25,3),uSpin:i(t.spin,-1,1),uStars:i(t.stars,.1,2),uSpeed:t.speed,uWarm:L(t,["#ffd9a0","#9fd0ff"],0),uCool:L(t,["#ffd9a0","#9fd0ff"],1),uBg:z(t.background||"#05060f")}},fragment:["float rlH(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }","vec2 rlH2(vec2 p){"," return fract(sin(vec2(dot(p, vec2(127.1,311.7)), dot(p, vec2(269.5,183.3)))) * 43758.5453);","}","float rlLayer(vec2 p, float sc, float sd, float dens, out float mag){"," vec2 g = p * sc + sd * 7.13;"," vec2 id = floor(g);"," vec2 f = fract(g) - 0.5;"," vec2 hh = rlH2(id + sd);"," mag = hh.x;"," if (rlH(id * 1.31 + sd * 3.7) > 0.34 * dens) return 0.0;"," float d = length(f - (hh - 0.5) * 0.62);"," float rr = mix(0.016, 0.052, mag * mag);"," float core = smoothstep(rr, rr * 0.15, d);"," float halo = 0.20 * exp(-d * d / (rr * rr * 5.0));"," return (core * 1.15 + halo) * mix(0.30, 1.0, mag);","}","float rlSky(vec2 p){ return 0.011 + 0.007 * sin(p.x * 6.0 + 1.3) * sin(p.y * 5.0 - 0.7); }","","void main(){"," vec2 res = uResolution;"," float mn = min(res.x, res.y);"," vec2 p = (gl_FragCoord.xy - 0.5 * res) / mn;"," float tt = uTime * uSpeed;"," vec2 bh = vec2(0.13 * sin(tt * 0.13), 0.08 * sin(tt * 0.11 + 1.7));"," if (uMouse.z > 0.5) bh = (uMouse.xy - 0.5 * res) / mn;",""," vec2 d = p - bh;"," float r = max(length(d), 1e-5);"," float rs = 0.050 * uMass;"," float bc = 2.598 * rs;"," float k = 20.0 * rs * rs;",""," vec2 src = d * (1.0 - k / (r * r)) + vec2(tt * 0.004, tt * 0.0016);"," float q = k / (r * r);"," float mu = min(1.0 / max(abs(1.0 - q * q), 0.02), 42.0);",""," float m0, m1, m2;"," float s = rlLayer(src, 8.0, 0.0, uStars, m0);"," s += 0.80 * rlLayer(src, 15.0, 3.0, uStars, m1);"," s += 0.55 * rlLayer(src, 27.0, 9.0, uStars, m2);"," float sky = rlSky(src);",""," vec3 starCol = mix(uCool, uWarm, m1 * m1);"," starCol = mix(starCol, vec3(1.0), 0.45 + 0.4 * m0);"," vec3 col = starCol * s * mu * 1.15;"," col += mix(uWarm, uCool, 0.25) * sky * mu * 1.45;",""," float rsh = length(d - vec2(uSpin * 0.30 * rs, 0.0));"," col *= smoothstep(bc * 0.99, bc * 1.05, rsh);",""," float ph = exp(-max(r - bc, 0.0) / (0.085 * bc));"," ph *= smoothstep(bc * 0.985, bc * 1.03, rsh);"," ph *= 1.0 + 0.8 * uSpin * (d.x / r);"," col += mix(uWarm, vec3(1.0), 0.35) * ph * 1.3;",""," col = 1.0 - exp(-col * 1.5);"," fragColor = vec4(min(vec3(1.0), uBg + col), 1.0);","}"].join("\n")}),m("accretionDisk",{defaults:{inclination:1.15,beta:.5,temperature:1,speed:1,colors:["#e01500","#ffa62b","#bfe0ff"],background:"#04050c"},staticTime:6,uniforms:function(t){var e=["#e01500","#ffa62b","#bfe0ff"];return{uInc:i(t.inclination,.15,1.48),uBeta:i(t.beta,.05,.85),uTemp:i(t.temperature,.3,2.5),uSpeed:t.speed,uC0:L(t,e,0),uC1:L(t,e,1),uC2:L(t,e,2),uBg:z(t.background||"#04050c")}},fragment:["vec3 adRamp(float x){"," x = clamp(x, 0.0, 1.0);"," return x < 0.5 ? mix(uC0, uC1, x * 2.0) : mix(uC1, uC2, (x - 0.5) * 2.0);","}","float adBand(float ph){ float w = fwidth(ph); return sin(ph) * exp(-0.3 * w * w); }","","const float RS = 0.045;","const float RIN = 3.0 * RS;","const float ROUT = 0.42;","","vec3 adDisk(vec2 dc, float si, float atten){"," float r = length(dc);"," float vis = smoothstep(RIN, RIN * 1.05, r) * smoothstep(ROUT, ROUT * 0.86, r);"," if (vis < 0.002) return vec3(0.0);"," float beta = min(uBeta * sqrt(RIN / r), 0.95);"," float gam = 1.0 / sqrt(1.0 - beta * beta);"," float cosl = (dc.x / r) * si;"," float dop = 1.0 / (gam * (1.0 - beta * cosl));"," float grav = sqrt(max(1.0 - RS / r, 0.02));"," float g = dop * grav;"," float g2 = g * g;"," float flux = pow(RIN / r, 3.0) * (1.0 - sqrt(RIN / r));"," float om = uSpeed * 1.15 * pow(RIN / r, 1.5);"," float a = atan(dc.y, dc.x) - om * uTime;"," float tex = 0.66 + 0.19 * adBand(a * 3.0 + r * 13.0)"," + 0.13 * adBand(a * 7.0 - r * 21.0 + 1.7)"," + 0.08 * adBand(a * 12.0 + r * 9.0);"," float temp = (pow(max(flux, 1e-4), 0.25) * g * uTemp - 0.24) * 2.5;"," return adRamp(temp) * (flux * tex * g2 * g2 * 15.0) * vis * atten;","}","","void main(){"," vec2 res = uResolution;"," float mn = min(res.x, res.y);"," vec2 p = (gl_FragCoord.xy - 0.5 * res) / mn;"," float ci = max(cos(uInc), 0.05), si = sin(uInc);"," float bc = 2.598 * RS;"," float scr = max(length(p), 1e-5);",""," vec2 b = p * (1.0 - 2.0 * RS * RS / (scr * scr));"," vec2 dc = vec2(b.x, -b.y / ci);"," vec3 col = adDisk(dc, si, 1.0);"," if (dc.y * si < 0.0) col *= smoothstep(bc * 0.97, bc * 1.05, scr);",""," float u = (scr - bc) / (0.5 * bc);"," if (u > 0.0 && u < 1.0) {"," vec2 dir = normalize(vec2(p.x, -p.y / ci));"," float w = (1.0 - u) * smoothstep(0.0, 0.12, u);"," col += adDisk(dir * (RIN + (ROUT - RIN) * pow(u, 2.6)), si, w * 1.1);"," }",""," col *= smoothstep(bc * 0.985, bc * 1.015, scr);"," float ring = exp(-max(scr - bc, 0.0) / (0.045 * bc));"," col += mix(uC1, vec3(1.0), 0.35) * ring * smoothstep(bc * 0.99, bc * 1.02, scr) * 1.15;"," col += uC1 * 0.05 * exp(-scr * 2.4);",""," col = 1.0 - exp(-col * 1.4);"," col = pow(col, vec3(0.75));"," fragColor = vec4(min(vec3(1.0), uBg + col), 1.0);","}"].join("\n")}),k("nBody",{defaults:{count:13,gravity:1,softening:1,trail:1,speed:1,colors:["#ff7a2f","#ffc978","#fff6e8","#bcd4ff"],background:"#05060f"},setup:function(t){var e,a,r,l,c,h,d,v,p,g,m,x,b,y,M,w,S,C,k,T,R,z=.0012,L=170,E=0,F=[.05,.1,.19,.4];function q(){var t,o;for(t=0;t4&&(.5*(c[t]*c[t]+h[t]*h[t])-C*a[0]/Math.sqrt(n)>0||n>26)&&D(t,o(.18,.62))}}function O(){y=u(t.opts,["#ff7a2f","#ffc978","#fff6e8","#bcd4ff"]),M=t.width/2,w=t.height/2,S=.54*Math.min(t.width,t.height),C=.9*(t.opts.gravity||1);var n,s=.021*i(t.opts.softening,.15,6);for(k=s*s,e=Math.max(4,Math.min(40,Math.round(t.opts.count))),a=[],r=[],l=[],c=[],h=[],d=[],v=[],p=[],g=[],x=[],b=[],m=0,E=0,R=0,n=0;nT&&(T=a[n]);for(n=0;n=z&&C<14;)B(),R-=z,C++;R>.4&&(R=0),c.globalCompositeOperation="lighter",c.lineCap="round";var k=i(t.opts.trail,0,1);for(u=1;u=F&&u<44;)O(),A-=F,u++;A>.3&&(A=0),(E+=i)>40&&I();var h=Math.sqrt(v*v+p*p);M=h*Math.pow(y/3,1/3),w=.5*(g*g+m*m)-R/Math.max(h,.001),c.globalCompositeOperation="lighter";var x=c.createRadialGradient(C,k,0,C,k,.2*T);x.addColorStop(0,s(S[2]||S[0],1)),x.addColorStop(.09,s(S[1]||S[0],.42)),x.addColorStop(.34,s(S[1]||S[0],.07)),x.addColorStop(1,"rgba(0,0,0,0)"),c.fillStyle=x,c.beginPath(),c.arc(C,k,.2*T,0,n()),c.fill();var b,z=C+v*T,L=k+p*T,q=M*M;for(b=0;b0?Math.pow(e,.25):.0475)>w?(t=Math.sqrt(.4/(f*f*f)),g=0,m=0):(t=.92*Math.sqrt(.4/(w*w*w)),g=i(1-(m+=b)/.9,0,1),m>.9&&(f=M,m=0,g=0)),h+=t*b,d[p=(p+1)%y]=h,v[p]=f>w?M/f:M/w*Math.exp(6.5*-m)}function k(t){var e=t/b,a=Math.floor(e),o=e-a;if(a<0&&(a=0,o=0),a>=3198)return!1;var r=(p-a+y+y)%y,i=(r-1+y)%y;return S[0]=d[r]+(d[i]-d[r])*o,S[1]=v[r]+(v[i]-v[r])*o,!0}function T(){e=u(t.opts,["#7fb2ff","#ff77b8","#ffffff"]),a=t.width/2,o=t.height/2,l=.5*Math.min(t.width,t.height),x=.00123*i(t.opts.decay,.1,6),f=M,h=0,c=0,g=0,m=0,p=0,d=[],v=[];for(var r=0;r=b&&p<600;)C(),c-=b,p++;c>.2&&(c=0),v.globalCompositeOperation="lighter";var m,x,y,w,T=.5*Math.hypot(t.width,t.height)/l,R=[];for(x=0;x<32;x++)R.push([]);for(m=0;m<4;m++){var z=m%2,L=m*Math.PI/2,A=0,E=0,F=!1,q=0;for(x=0;x<=170;x++){var D=x/170,B=.03+(T-.03)*D*D;if(k(B/.17)){var O=S[0]+L,I=S[1],G=a+Math.cos(O)*B*l,_=o+Math.sin(O)*B*l;if(F){var W=Math.abs(O-q),U=i(I/(.35+2*B)*.55*Math.exp(-W*W*4),0,1),V=Math.min(15,Math.floor(16*U*1.3));(V=Math.max(0,V))>0&&R[16*z+V].push(A,E,G,_)}A=G,E=_,q=O,F=!0}else F=!1}}v.lineCap="round";for(var H=0;H<2;H++)for(y=0;y<2;y++)for(w=1;w<16;w++){var N=R[16*y+w];if(N.length){var j=(w+.6)/16.32;for(v.strokeStyle=s(e[y]||e[0],H?j:.16*j),v.lineWidth=H?.8+.13*w:3.5+.4*w,v.beginPath(),x=0;x0){var tt=1-g;v.strokeStyle=s(X,.85*g),v.lineWidth=r(11,.5,tt),v.beginPath(),v.arc(a,o,tt*Math.hypot(a,o)*1.15,0,n()),v.stroke();var et=.55*l*g,at=v.createRadialGradient(a,o,0,a,o,et);at.addColorStop(0,s(X,g)),at.addColorStop(1,"rgba(0,0,0,0)"),v.fillStyle=at,v.beginPath(),v.arc(a,o,et,0,n()),v.fill()}v.globalCompositeOperation="source-over"}}}});var B=["float bvH1(vec3 p){"," p = fract(p * 0.1031);"," p += dot(p, p.zyx + 31.32);"," return fract((p.x + p.y) * p.z);","}","vec3 bvH3(vec3 p){"," p = fract(p * vec3(0.1031, 0.1030, 0.0973));"," p += dot(p, p.yxz + 33.33);"," return fract((p.xxy + p.yxx) * p.zyx);","}","float bvN(vec3 x){"," vec3 i = floor(x); vec3 f = fract(x);"," f = f * f * (3.0 - 2.0 * f);"," return mix(mix(mix(bvH1(i), bvH1(i + vec3(1.0, 0.0, 0.0)), f.x),"," mix(bvH1(i + vec3(0.0, 1.0, 0.0)), bvH1(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),"," mix(mix(bvH1(i + vec3(0.0, 0.0, 1.0)), bvH1(i + vec3(1.0, 0.0, 1.0)), f.x),"," mix(bvH1(i + vec3(0.0, 1.0, 1.0)), bvH1(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);","}","mat2 bvRot(float a){ float s = sin(a); float c = cos(a); return mat2(c, s, -s, c); }","float bvFbm4(vec3 p){"," float a = 0.5; float s = 0.0;"," for (int i = 0; i < 4; i++) {"," s += a * bvN(p);"," p.xy = bvRot(1.09) * p.xy;"," p = p * 2.04 + vec3(1.7, 9.2, 4.3);"," a *= 0.53;"," }"," return s * 1.03;","}","float bvFbm3(vec3 p){"," float s = 0.5 * bvN(p);"," p.xy = bvRot(1.09) * p.xy; p = p * 2.04 + vec3(1.7, 9.2, 4.3);"," s += 0.27 * bvN(p);"," p.xy = bvRot(1.09) * p.xy; p = p * 2.04 + vec3(1.7, 9.2, 4.3);"," s += 0.15 * bvN(p);"," return s * 1.09;","}","float bvFbm2(vec3 p){"," float s = 0.5 * bvN(p);"," p.xy = bvRot(1.09) * p.xy;"," s += 0.28 * bvN(p * 2.04 + vec3(1.7, 9.2, 4.3));"," return s * 1.28;","}","float bvHG(float c, float g){"," float g2 = g * g;"," return (1.0 - g2) / pow(max(1.0 + g2 - 2.0 * g * c, 1e-4), 1.5);","}","vec2 bvSph(vec3 ro, vec3 rd, float ra){"," float b = dot(ro, rd);"," float k = dot(ro, ro) - ra * ra;"," float h = b * b - k;"," if (h < 0.0) return vec2(1.0, -1.0);"," h = sqrt(h);"," return vec2(-b - h, -b + h);","}","vec3 bvTone(vec3 x){"," x = max(x, vec3(0.0));"," x = (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);"," return pow(clamp(x, 0.0, 1.0), vec3(0.90));","}","float bvDith(vec2 c){ return fract(sin(dot(c, vec2(12.9898, 78.233))) * 43758.5453); }","vec2 bvUV(){ return (gl_FragCoord.xy - 0.5 * uResolution) / min(uResolution.x, uResolution.y) * 2.0; }","vec2 bvMUV(){ return (uMouse.xy - 0.5 * uResolution) / min(uResolution.x, uResolution.y) * 2.0; }","float bvStars(vec3 rd, float amt){"," vec3 p = rd * 170.0;"," vec3 i = floor(p); vec3 f = fract(p) - 0.5;"," vec3 o = bvH3(i) - 0.5;"," float d = length(f - o * 0.72);"," float m = bvH1(i + 7.31);"," float br = smoothstep(0.93 - 0.10 * amt, 1.0, m);"," return br * exp(-d * d * 240.0) * (0.35 + 0.65 * m);","}"].join("\n");function O(t,e,a,o,r,i,n){t.font="10px ui-monospace, SFMono-Regular, Menlo, monospace",t.textAlign=n||"left",t.textBaseline="alphabetic",t.fillStyle=s(r,i),t.fillText(e,Math.round(a),Math.round(o)),t.textAlign="left"}function I(t,e,a,o,r,i,n,l){if(t.strokeStyle=s(i,n),t.lineWidth=l||1,t.beginPath(),a===r){var c=Math.round(a)+.5;t.moveTo(e,c),t.lineTo(o,c)}else if(e===o){var f=Math.round(e)+.5;t.moveTo(f,a),t.lineTo(f,r)}else t.moveTo(e,a),t.lineTo(o,r);t.stroke()}function G(t,e,a,o,r,i,n){t.strokeStyle=s(i,n),t.lineWidth=1,t.strokeRect(Math.round(e)+.5,Math.round(a)+.5,Math.round(o),Math.round(r))}function _(t,e){var a=l(t.opts.background||"#04060f");return{bg:a,grid:f(a,e[0],.22),axis:f(a,e[0],.5),label:f(e[0],{r:255,g:255,b:255},.5),hi:e[e.length-1],data:e[0]}}function W(t){var e=43758.5453*Math.sin(127.1*t+311.7);return e-Math.floor(e)}function U(){return 2*(Math.random()+Math.random()+Math.random()-1.5)}function V(t){var e,a,o,r=i(t,1e3,4e4)/100;return e=r<=66?255:i(329.698727446*Math.pow(r-60,-.1332047592),0,255),a=i(r<=66?99.4708025861*Math.log(r)-161.1195681661:288.1221695283*Math.pow(r-60,-.0755148492),0,255),o=r>=66?255:r<=19?0:i(138.5177312231*Math.log(r-10)-305.0447927307,0,255),{r:Math.round(e),g:Math.round(a),b:Math.round(o)}}function H(t){var e=0,a=0,o=0,r=1;return t>=380&&t<440?(e=-(t-440)/60,o=1):t<490?(a=(t-440)/50,o=1):t<510?(a=1,o=-(t-510)/20):t<580?(e=(t-510)/70,a=1):t<645?(e=1,a=-(t-645)/65):t<=781&&(e=1),t<420?r=.25+.75*(t-380)/40:t>700&&(r=.25+.75*(781-t)/81),{r:Math.round(255*Math.pow(i(e*r,0,1),.85)),g:Math.round(255*Math.pow(i(a*r,0,1),.85)),b:Math.round(255*Math.pow(i(o*r,0,1),.85))}}function N(t,e){var a=14387769/(t*e);return a>60?0:1/(Math.pow(.001*t,5)*(Math.exp(a)-1))}function j(t,e,a){var o=document.createElement(t);return e&&(o.className=e),null!=a&&(o.innerHTML=a),o}m("volumetricNebula",{defaults:{speed:1,density:1,absorption:1,anisotropy:.6,interactive:!0,colors:["#ff4f96","#31c9f5","#ffe6c2"],background:"#03030c"},staticTime:14,uniforms:function(t){return{uSpeed:q(t.speed,1,0,6),uDensity:q(t.density,1,.05,4),uAbsorb:2.9*q(t.absorption,1,.05,5),uAniso:q(t.anisotropy,.6,-.9,.9),uColA:F(t,0,"#ff4f96"),uColB:F(t,1,"#31c9f5"),uLight:F(t,2,"#ffe6c2"),uBg:E(t.background||"#03030c")}},fragment:[B,"float bvShape(vec3 p){"," vec3 q = p;"," q.xz = bvRot(0.62) * q.xz;"," q.xy = bvRot(-0.22) * q.xy;"," float r = length(q * vec3(0.52, 1.12, 0.78));"," return smoothstep(1.55, 0.10, r);","}","vec3 bvWarp(vec3 p, float t){"," return p + 0.30 * vec3(sin(p.y * 2.05 + t * 0.21),"," sin(p.z * 1.71 - t * 0.17),"," sin(p.x * 2.37 + t * 0.13));","}","float bvCurve(float f, float sh){"," return pow(max(f - 0.505, 0.0) * 4.6, 1.6) * sh;","}","float bvDens(vec3 p, float t){"," return bvCurve(bvFbm4(bvWarp(p, t) * 1.18 + vec3(0.0, -t * 0.035, t * 0.02)), bvShape(p));","}","float bvDensLo(vec3 p, float t){"," return bvCurve(bvFbm2(bvWarp(p, t) * 1.18 + vec3(0.0, -t * 0.035, t * 0.02)), bvShape(p));","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," float ca = t * 0.042;"," vec3 ro = vec3(0.0, 0.12 * sin(t * 0.06), -3.05);"," vec3 rd = normalize(vec3(uv * 0.58, 1.28));"," ro.xz = bvRot(ca) * ro.xz;"," rd.xz = bvRot(ca) * rd.xz;"," float tl = 0.09 * sin(t * 0.05);"," ro.yz = bvRot(tl) * ro.yz;"," rd.yz = bvRot(tl) * rd.yz;"," vec3 L = vec3(0.55 * sin(t * 0.11), 0.24 * sin(t * 0.083), 0.40 * cos(t * 0.11));"," if (uMouse.z > 0.5) {"," vec2 m = bvMUV();"," L = mix(L, vec3(m.x * 1.05, m.y * 0.90, 0.10), 0.85);"," }"," vec3 col = vec3(0.0);"," float T = 1.0;"," vec2 hit = bvSph(ro, rd, 2.05);"," if (hit.y > hit.x && hit.y > 0.0) {"," float t0 = max(hit.x, 0.0);"," float ds = (hit.y - t0) / 40.0;"," float tt = t0 + ds * dth;"," for (int i = 0; i < 40; i++) {"," if (T < 0.010) break;"," vec3 p = ro + rd * tt;"," float d = bvDens(p, t) * uDensity;"," vec3 pl = L - p;"," float dl = max(length(pl), 1e-4);"," if (d > 0.004) {"," vec3 ld = pl / dl;"," vec3 sp = p; float sh = 0.0;"," for (int j = 0; j < 3; j++) { sp += ld * 0.26; sh += bvDensLo(sp, t) * uDensity; }"," float shT = exp(-sh * 0.26 * uAbsorb * 1.5);"," float ph = bvHG(dot(rd, ld), uAniso);"," float fall = 1.9 / (0.16 + dl * dl);"," vec3 alb = mix(uColB, uColA, smoothstep(0.05, 0.75, d));"," vec3 S = alb * uLight * shT * ph * fall * 2.30"," + uColB * 0.055"," + uColA * 0.30 * smoothstep(0.45, 1.6, d);"," col += T * S * d * ds;"," T *= exp(-d * ds * uAbsorb);"," }"," col += T * uLight * (0.0065 / (0.0022 + dl * dl * dl)) * ds * 1.4;"," tt += ds;"," }"," }"," vec3 bg = uBg;"," bg += vec3(bvStars(rd, 0.6)) * vec3(0.92, 0.95, 1.0) * 1.7;"," bg += vec3(bvStars(rd * 2.4 + 11.0, 0.3)) * vec3(0.7, 0.8, 1.0) * 0.8;"," bg += mix(uColB, uColA, 0.4) * 0.030 * (1.0 - 0.45 * length(uv));"," col += T * bg;"," col = bvTone(col * 1.30);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),m("starSurface",{defaults:{speed:1,temperature:5200,granulation:1,spots:.55,interactive:!0,colors:["#ff5a1e","#ffd9a0"],background:"#05030a"},staticTime:11,uniforms:function(t){var e=q(t.temperature,5200,1800,16e3);return{uSpeed:q(t.speed,1,0,6),uGran:q(t.granulation,1,0,1),uSpots:q(t.spots,.55,0,1),uHot:D(1.06*e,1),uWarm:D(.62*e,.3),uFlare:F(t,0,"#ff5a1e"),uBg:E(t.background||"#05030a")}},fragment:[B,"vec3 bvWorley(vec3 p, float t){"," vec3 ip = floor(p); vec3 fp = fract(p);"," float f1 = 9.0; float f2 = 9.0; float id = 0.0;"," for (int z = -1; z <= 1; z++) {"," for (int y = -1; y <= 1; y++) {"," for (int x = -1; x <= 1; x++) {"," vec3 g = vec3(float(x), float(y), float(z));"," vec3 o = bvH3(ip + g);"," float d = length(g + 0.5 + 0.40 * sin(t + 6.2831853 * o) - fp);"," if (d < f1) { f2 = f1; f1 = d; id = o.y; } else if (d < f2) { f2 = d; }"," }"," }"," }"," return vec3(f1, f2, id);","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float R = 0.80;"," float r = length(uv);"," float px = 2.2 / min(uResolution.x, uResolution.y);"," vec3 col = uBg;"," vec3 rd3 = normalize(vec3(uv, 1.5));"," col += vec3(bvStars(rd3, 0.4)) * 1.1;"," float ang = atan(uv.y, uv.x);"," float cr = max(r / R, 1.0);"," float stream = 0.35 + 0.65 * bvFbm2(vec3(cos(ang) * 2.1, sin(ang) * 2.1, t * 0.04) * 1.9);"," col += uFlare * pow(1.0 / cr, 5.5) * stream * 0.22;"," col += vec3(1.0, 0.72, 0.42) * pow(1.0 / cr, 13.0) * 0.20;"," float disk = smoothstep(R + px, R - px, r);"," if (disk > 0.001) {"," float mu = sqrt(max(1.0 - (r / R) * (r / R), 0.0));"," vec3 sp = vec3(uv / R, mu);"," sp.yz = bvRot(-0.34) * sp.yz;"," sp.xz = bvRot(t * 0.030) * sp.xz;"," vec3 w1 = bvWorley(sp * 17.0, t * 1.00);"," vec3 w2 = bvWorley(sp * 41.0 + 17.0, t * 1.75);"," float lane1 = smoothstep(0.0, 0.20, w1.y - w1.x);"," float lane2 = smoothstep(0.0, 0.26, w2.y - w2.x);"," float cellB = 0.62 + 0.38 * w1.z;"," float pepper = 0.90 + 0.10 * bvN(sp * 90.0 + t * 0.6);"," float sgran = 0.80 + 0.30 * bvFbm2(sp * 4.6 + vec3(0.0, 0.0, t * 0.05));"," float cells = lane1 * cellB * (0.74 + 0.26 * lane2) * pepper * sgran;"," float gran = mix(0.72, cells, clamp(uGran, 0.0, 1.0));"," float sv = bvFbm3(sp * 2.30 + 31.0);"," float thr = 0.72 - 0.20 * uSpots;"," float pen = smoothstep(thr - 0.09, thr + 0.09, sv);"," float umb = smoothstep(thr + 0.05, thr + 0.15, sv);"," float fil = 0.45 + 0.55 * bvN(sp * 58.0 + t * 0.30);"," float spotDark = mix(1.0, mix(0.30 * fil + 0.05, 0.035, umb), pen);"," gran = mix(gran, 0.26 + 0.14 * gran, pen);"," float limb = (1.0 - 0.78 * (1.0 - mu)) * (0.42 + 0.58 * pow(mu, 0.40));"," vec3 surf = mix(uWarm, uHot, clamp(gran, 0.0, 1.0));"," surf += uHot * pow(clamp(gran, 0.0, 1.0), 6.0) * 0.42;"," surf *= limb * spotDark;"," surf += uHot * (1.0 - lane1) * pow(1.0 - mu, 2.4) * 0.55 * (1.0 - pen);"," if (uMouse.z > 0.5) {"," vec2 m = bvMUV();"," float pd = length(uv - m);"," float plage = exp(-pd * pd * 30.0) * disk;"," surf += uHot * plage * 0.9 + vec3(1.0, 0.93, 0.8) * plage * plage * 0.8;"," }"," col = mix(col, surf, disk);"," }"," float above = max(r - R, 0.0);"," col += uFlare * exp(-above * 170.0) * smoothstep(R - px * 2.0, R + px, r) * 1.1;"," float pr = 0.0;"," for (int i = 0; i < 3; i++) {"," float fi = float(i);"," float base = 1.15 + fi * 2.24 + t * 0.030;"," float span = 0.24 + 0.09 * sin(t * 0.21 + fi * 1.7);"," float ht = 0.17 + 0.09 * sin(t * 0.27 + fi * 2.3);"," vec2 upv = vec2(cos(base), sin(base));"," vec2 c1 = vec2(cos(base - span), sin(base - span)) * R;"," vec2 c2 = vec2(cos(base + span), sin(base + span)) * R;"," vec2 mid = 0.5 * (c1 + c2);"," float hw = 0.5 * length(c2 - c1);"," float rr = (hw * hw + ht * ht) / (2.0 * ht);"," vec2 cc = mid + normalize(mid) * (ht - rr);"," float dd = abs(length(uv - cc) - rr);"," float sect = smoothstep(cos(span * 3.1), cos(span * 1.5), dot(normalize(uv + 1e-5), upv));"," float wob = 0.30 + 0.90 * bvFbm2(vec3(uv * 9.0, t * 0.4 + fi * 9.0));"," float outside = smoothstep(R * 0.965, R * 1.01, r) * exp(-above * 5.5);"," pr += exp(-dd * dd * 1600.0) * sect * wob * outside;"," }"," col += uFlare * pr * 1.7 + vec3(1.0, 0.70, 0.46) * pr * pr * 0.8;"," col = bvTone(col * 1.02);"," col += (bvDith(gl_FragCoord.xy) - 0.5) / 220.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),m("atmosphere",{defaults:{speed:1,radius:1,thickness:.1,rayleigh:1,mie:1,interactive:!0,colors:["#6ff0d0","#3d7a52"],background:"#02030a"},staticTime:8,uniforms:function(t){return{uSpeed:q(t.speed,1,0,6),uRadius:q(t.radius,1,.25,3),uThick:q(t.thickness,.1,.02,.3),uRay:.42*q(t.rayleigh,1,0,4),uMie:.42*q(t.mie,1,0,4),uGlow:F(t,0,"#6ff0d0"),uLand:F(t,1,"#3d7a52"),uBg:E(t.background||"#02030a")}},fragment:[B,"void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," float R = 1.0;"," float Ra = R + uThick;"," float D = 1.0 + 0.92 / uRadius;"," vec3 ro = vec3(0.0, 0.0, -D);"," vec3 rd = normalize(vec3(uv, 1.55));"," rd.yz = bvRot(-0.60) * rd.yz;"," float sa = 1.32 + t * 0.15;"," float se = 0.20 + 0.12 * sin(t * 0.085);"," if (uMouse.z > 0.5) { vec2 m = bvMUV(); sa = 1.55 + m.x * 2.3; se = m.y * 0.95; }"," vec3 sun = normalize(vec3(sin(sa) * cos(se), sin(se), cos(sa) * cos(se)));"," vec2 ta = bvSph(ro, rd, Ra);"," vec2 tp = bvSph(ro, rd, R);"," bool onPlanet = (tp.y > tp.x) && (tp.x > 0.0);"," vec3 base = uBg;"," base += vec3(bvStars(rd, 0.55)) * 1.7;"," base += vec3(bvStars(rd * 2.6 + 5.0, 0.30)) * vec3(0.78, 0.85, 1.0) * 0.85;"," if (onPlanet) {"," vec3 hp = ro + rd * tp.x;"," vec3 n = normalize(hp);"," float ndl = dot(n, sun);"," float lam = max(ndl, 0.0);"," float land = bvFbm3(n * 1.95 + 4.0);"," float isLand = smoothstep(0.520, 0.575, land);"," vec3 alb = mix(vec3(0.008, 0.030, 0.086), uLand, isLand);"," alb = mix(alb, uLand * vec3(1.9, 1.45, 0.80), isLand * smoothstep(0.60, 0.70, land) * 0.8);"," float ice = smoothstep(0.76, 0.93, abs(n.y));"," alb = mix(alb, vec3(0.78, 0.83, 0.88), ice);"," float cl = smoothstep(0.585, 0.760, bvFbm3(n * 2.70 + vec3(t * 0.045, 0.0, 0.0)));"," alb = mix(alb, vec3(0.90, 0.92, 0.96), cl * 0.88);"," float spec = pow(max(dot(reflect(-sun, n), -rd), 0.0), 60.0) * (1.0 - isLand) * (1.0 - cl);"," base = alb * lam * 2.10 + vec3(1.0, 0.95, 0.86) * spec * 2.6;"," float city = smoothstep(0.64, 0.82, bvFbm3(n * 7.3 + 20.0)) * isLand * (1.0 - ice);"," base += vec3(1.0, 0.66, 0.30) * city * smoothstep(0.04, -0.26, ndl) * 0.95 * (1.0 - cl);"," }"," vec3 col = base;"," if (ta.y > ta.x && ta.y > 0.0) {"," float t0 = max(ta.x, 0.0);"," float t1 = onPlanet ? tp.x : ta.y;"," if (t1 > t0) {"," float ds = (t1 - t0) / 24.0;"," float tt = t0 + ds * dth;"," float Hr = uThick * 0.33;"," float Hm = uThick * 0.11;"," vec3 bR = vec3(5.80, 13.50, 33.10) * uRay;"," float bM = 21.0 * uMie;"," float odR = 0.0; float odM = 0.0; float glow = 0.0;"," vec3 sumR = vec3(0.0); vec3 sumM = vec3(0.0);"," for (int i = 0; i < 24; i++) {"," vec3 p = ro + rd * tt;"," float hh = length(p) - R;"," float rr = exp(-hh / Hr) * ds;"," float mm = exp(-hh / Hm) * ds;"," odR += rr; odM += mm;"," float nl = dot(normalize(p), sun);"," float ag = exp(-pow((hh - uThick * 0.36) / (uThick * 0.11), 2.0));"," glow += ag * smoothstep(0.18, -0.28, nl) * ds;"," vec2 tps = bvSph(p, sun, R);"," bool shadowed = (tps.y > tps.x) && (tps.x > 0.0);"," if (!shadowed) {"," vec2 tl = bvSph(p, sun, Ra);"," float dls = max(tl.y, 0.0) / 6.0;"," float lt = dls * 0.5;"," float lR = 0.0; float lM = 0.0;"," for (int j = 0; j < 6; j++) {"," float hj = length(p + sun * lt) - R;"," lR += exp(-hj / Hr) * dls;"," lM += exp(-hj / Hm) * dls;"," lt += dls;"," }"," vec3 att = exp(-(bR * (odR + lR) + bM * 1.1 * (odM + lM)));"," sumR += rr * att;"," sumM += mm * att;"," }"," tt += ds;"," }"," float cth = dot(rd, sun);"," float phR = 0.75 * (1.0 + cth * cth);"," float phM = bvHG(cth, 0.76) * 0.075;"," vec3 scat = (sumR * bR * phR + sumM * bM * phM) * 5.2;"," vec3 ext = exp(-(bR * odR + bM * odM));"," col = base * ext + scat + uGlow * glow * 5.5;"," }"," }"," col = bvTone(col * 1.08);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),m("dustLanes",{defaults:{speed:1,density:1,shadowSteps:4,extinction:1,interactive:!0,colors:["#ffd6a5","#8ec5ff"],background:"#04050e"},staticTime:12,uniforms:function(t){return{uSpeed:q(t.speed,1,0,6),uDensity:q(t.density,1,.05,4),uShadow:Math.round(q(t.shadowSteps,4,0,6)),uExt:2.45*q(t.extinction,1,.1,4),uWarm:F(t,0,"#ffd6a5"),uCool:F(t,1,"#8ec5ff"),uBg:E(t.background||"#04050e")}},fragment:[B,"float bvShapeD(vec3 p, float f){"," float body = smoothstep(0.505, 0.700, f);"," float fil = pow(1.0 - abs(f * 2.0 - 1.0), 6.0);"," float y = p.y + 0.62 * sin(p.x * 0.42 + 0.7) + 0.22 * sin(p.z * 0.5);"," float slab = exp(-y * y * 2.6);"," return (body * 1.15 + fil * 0.55) * slab * 2.25;","}","float bvDust(vec3 p, float t){"," vec3 q = p; q.x -= t * 0.17; q.y += t * 0.035;"," return bvShapeD(p, bvFbm4(q * 0.85));","}","float bvDustLo(vec3 p, float t){"," vec3 q = p; q.x -= t * 0.17; q.y += t * 0.035;"," return bvShapeD(p, bvFbm2(q * 0.85));","}","vec3 bvField(vec3 rd){"," vec3 n = normalize(rd);"," vec2 s = n.xy / max(n.z, 0.2);"," vec3 c = uBg;"," float b = (s.y + 0.05 + 0.10 * sin(s.x * 4.2)) * 4.6;"," c += uWarm * exp(-b * b) * 0.55;"," vec2 dc = s - vec2(0.13, -0.02);"," c += uWarm * exp(-dot(dc, dc) * 26.0) * 1.55;"," c += mix(uCool, vec3(1.0), 0.55) * exp(-dot(dc, dc) * 5.0) * 0.20;"," c += vec3(bvStars(n, 0.66)) * 2.4;"," c += vec3(bvStars(n * 2.7 + 9.0, 0.36)) * mix(uCool, vec3(1.0), 0.4) * 1.1;"," return c;","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," vec3 ro = vec3(0.30 * sin(t * 0.045), 0.08 * sin(t * 0.031), -2.6);"," vec3 rd = normalize(vec3(uv * 0.60, 1.35));"," rd.xz = bvRot(0.16 * sin(t * 0.10)) * rd.xz;"," vec3 L = vec3(-1.05, 0.75, 0.20);"," if (uMouse.z > 0.5) { vec2 m = bvMUV(); L = vec3(m.x * 1.7, m.y * 1.3, 0.15); }"," vec3 extTint = vec3(0.52, 0.82, 1.42);"," vec3 T = vec3(1.0);"," vec3 col = vec3(0.0);"," float ds = 0.115;"," float tt = 0.55 + ds * dth;"," for (int i = 0; i < 42; i++) {"," if (T.r < 0.008) break;"," vec3 p = ro + rd * tt;"," float d = bvDust(p, t) * uDensity;"," if (d > 0.004) {"," vec3 pl = L - p;"," float dl = max(length(pl), 1e-4);"," vec3 ld = pl / dl;"," float sh = 0.0;"," vec3 sp = p;"," for (int j = 0; j < 6; j++) {"," if (float(j) >= uShadow) break;"," sp += ld * 0.22;"," sh += bvDustLo(sp, t) * uDensity;"," }"," float shT = exp(-sh * 0.22 * uExt * 0.9);"," float ph = bvHG(dot(rd, ld), 0.42);"," float fall = 2.2 / (0.45 + dl * dl);"," vec3 scat = uCool * shT * ph * fall * 0.52 + uWarm * 0.05;"," col += T * scat * d * ds;"," T *= exp(-d * ds * uExt * extTint);"," }"," tt += ds;"," }"," col += T * bvField(rd);"," col = bvTone(col * 1.32);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),m("protoplanetary",{defaults:{speed:1,inclination:62,gaps:3,flare:.3,interactive:!0,colors:["#ffb066","#8fb6ff","#fff2dc"],background:"#03030b"},staticTime:16,uniforms:function(t){var e=q(t.inclination,62,0,88);return{uSpeed:q(t.speed,1,0,6),uElev:(90-e)*Math.PI/180,uGaps:Math.round(q(t.gaps,3,0,4)),uFlare:q(t.flare,.3,0,1),uColA:F(t,0,"#ffb066"),uColB:F(t,1,"#8fb6ff"),uStar:F(t,2,"#fff2dc"),uBg:E(t.background||"#03030b")}},fragment:[B,"const float BV_RIN = 0.30;","const float BV_ROUT = 2.45;","float bvH(float r){ return 0.055 * pow(max(r, 0.05), 1.0 + uFlare * 1.5); }","float bvDisk(vec3 p, float t){"," float r = length(p.xz);"," if (r < BV_RIN || r > BV_ROUT) return 0.0;"," float H = bvH(r);"," float z = p.y / H;"," float vert = exp(-0.5 * z * z);"," float sig = pow(r, -0.80)"," * smoothstep(BV_RIN, BV_RIN * 1.55, r)"," * smoothstep(BV_ROUT, BV_ROUT * 0.62, r);"," for (int i = 0; i < 4; i++) {"," float on = step(float(i) + 0.5, uGaps);"," float rg = 0.58 + float(i) * 0.46;"," float w = 0.062 + 0.016 * float(i);"," float g = (r - rg) / w;"," sig *= 1.0 - on * 0.95 * exp(-g * g);"," float go = (r - rg - w * 2.1) / (w * 1.5);"," sig *= 1.0 + on * 0.45 * exp(-go * go);"," }"," float a = atan(p.z, p.x) - t * 0.50 * pow(max(r, 0.18), -1.5);"," float tex = 0.50 + 0.50 * bvFbm2(vec3(cos(a) * r * 2.1, p.y * 2.2, sin(a) * r * 2.1) * 1.25);"," return sig * vert * tex * 5.0;","}","void main(){"," vec2 uv = bvUV();"," float t = uTime * uSpeed;"," float dth = bvDith(gl_FragCoord.xy);"," vec3 ro = vec3(0.0, 0.0, -5.4);"," vec3 rd = normalize(vec3(uv, 2.05));"," float elev = uElev;"," if (uMouse.z > 0.5) { vec2 m = bvMUV(); elev = clamp(elev + m.y * 0.45, 0.04, 1.52); }"," ro.yz = bvRot(elev) * ro.yz; rd.yz = bvRot(elev) * rd.yz;"," float spin = t * 0.020;"," ro.xz = bvRot(spin) * ro.xz; rd.xz = bvRot(spin) * rd.xz;"," vec3 col = uBg;"," col += vec3(bvStars(rd, 0.5)) * 1.6;"," col += vec3(bvStars(rd * 2.5 + 7.0, 0.28)) * vec3(0.8, 0.86, 1.0) * 0.75;"," float T = 1.0;"," vec3 acc = vec3(0.0);"," vec2 hit = bvSph(ro, rd, BV_ROUT + 0.15);"," if (hit.y > hit.x && hit.y > 0.0) {"," float t0 = max(hit.x, 0.0);"," float ds = (hit.y - t0) / 46.0;"," float tt = t0 + ds * dth;"," for (int i = 0; i < 46; i++) {"," if (T < 0.010) break;"," vec3 p = ro + rd * tt;"," float dd = dot(p, p);"," float d = bvDisk(p, t);"," if (d > 0.004) {"," float rr = max(sqrt(dd), 0.12);"," vec3 toStar = -p / rr;"," float irr = 1.0 / (0.20 + rr * rr * 0.55);"," float tau = 0.0;"," float sds = rr / 3.0;"," vec3 sp = p;"," for (int j = 0; j < 3; j++) { sp += toStar * sds; tau += bvDisk(sp, t); }"," float shade = exp(-tau * sds * 2.4);"," float ph = bvHG(dot(p / rr, -rd), 0.52);"," vec3 tint = mix(uColA, uColB, smoothstep(BV_RIN * 1.5, BV_ROUT * 0.80, rr));"," vec3 S = tint * (irr * shade * ph * 0.42 + 0.045);"," acc += T * S * d * ds;"," T *= exp(-d * ds * 2.35);"," }"," acc += T * uStar * exp(-dd * 150.0) * ds * 4.5;"," acc += T * uStar * (0.0016 / (0.004 + dd * dd)) * ds * 0.55;"," tt += ds;"," }"," }"," col = col * T + acc;"," col = bvTone(col * 1.16);"," col += (dth - 0.5) / 200.0;"," fragColor = vec4(col, 1.0);","}"].join("\n")}),k("spectrograph",{defaults:{temperature:5778,lines:!0,speed:1,interactive:!0,colors:["#22d3ee","#7c5cff"],background:"#04060f"},setup:function(t){for(var e,a,o,l,c,f,h,d,v,p,g,m,x,b,y=380,M={y:0,h:0},w={y:0,h:0},S=[{w:393.37,n:"Ca K",d:.72,s:1.7,tag:1},{w:396.85,n:"Ca H",d:.62,s:1.5,tag:0},{w:410.17,n:"Hδ",d:.38,s:1,tag:0},{w:434.05,n:"Hγ",d:.44,s:1.1,tag:0},{w:438.36,n:"Fe",d:.3,s:.7,tag:0},{w:486.13,n:"Hβ",d:.55,s:1.3,tag:1},{w:516.73,n:"Mg b",d:.5,s:1.5,tag:1},{w:527.04,n:"Fe",d:.32,s:.8,tag:0},{w:588.99,n:"Na D",d:.58,s:1,tag:1},{w:589.59,n:"Na D2",d:.52,s:1,tag:0},{w:630.25,n:"Fe",d:.24,s:.6,tag:0},{w:656.28,n:"Hα",d:.66,s:1.9,tag:1},{w:686.72,n:"O2",d:.3,s:.9,tag:0}],C=[],k=0;k<90;k++)C.push({w:y+320*W(3.1*k),d:.05+.17*W(7.7*k),s:.35+.5*W(5.3*k)});function T(t){return y+(t-o)/(l-o)*320}function P(t){return o+(t-y)/320*(l-o)}function z(){var r,n,s;for(e=u(t.opts,["#22d3ee","#7c5cff"]),a=_(t,e),o=34,l=t.width-12,M.y=25,M.h=Math.max(20,Math.round(.135*t.height)),w.y=M.y+M.h+22,w.h=Math.max(50,t.height-w.y-66),c=t.opts.temperature,b=Math.max(8,Math.round(l-o)),h=0,r=0;r<=320;r++){var k=N(y+r,c);k>h&&(h=k)}for(s=t.opts.temperature,f={balmer:.35+.9*Math.exp(-Math.pow(Math.log(s/9500)/.42,2)),metal:.8*i((7500-s)/3600,0,1)+.35},d=[],v=[],r=0;r=1)for(m-=1,p=g,g=[],u=0;u=3e4?"O":C>=1e4?"B":C>=7500?"A":C>=6e3?"F":C>=5200?"G":C>=3700?"K":"M")+"-type",l,15,a.label,.55,"right");var L=[];for(u=0;ul-8||(I(k,h,M.y+M.h,h,M.y+M.h+5,a.label,.6),O(k,S[u].n,h,M.y+M.h+16,a.label,.82,"center")));var q=w.y,D=w.h,B=1.12,_=[0,.5,1];for(u=0;u<_.length;u++)y=q+D-_[u]/B*D,I(k,o,y,l,y,a.grid,0===u?.85:.5),O(k,_[u].toFixed(1),o-6,y+3.5,a.label,.5,"right");for(k.setLineDash([3,3]),k.strokeStyle=s(a.hi,.6),k.lineWidth=1,k.beginPath(),u=0;ul-14;O(k,String(N),j?l:h,V+15,a.label,.6,j?"right":"center")}O(k,"WAVELENGTH (nm)",l,V+27,a.label,.42,"right"),!1!==t.opts.interactive&&t.mouse.active?(W=i((t.mouse.x-o)/(l-o),0,1),x=W):((x+=.055*f*z)>1.4&&(x-=1.4),W=i(x,0,1));var K=o+W*(l-o),J=T(K),X=i(Math.round(W*(b-1)),0,b-1);k.setLineDash([2,3]),I(k,K,M.y,K,q+D,a.hi,.75),k.setLineDash([]);var Y=q+D-i(L[X],0,B)/B*D;k.fillStyle=s(a.hi,1),k.beginPath(),k.arc(K,Y,2.6,0,n()),k.fill();var Q=null,Z=4.2;for(u=0;u=1?0:1-r*(1-Math.sqrt(1-t*t))}function z(t){if(t>=1+o)return 1;var e,a,i=0,l=o/13;for(e=0;e<13;e++){var s=(e+.5)*l;for(a=0;a<26;a++){var c=(a+.5)/26*n();i+=P(Math.sqrt(t*t+s*s+2*t*s*Math.cos(c)))*s}}return 1-(i*=l*(n()/26))/(Math.PI*(1-r/3))}function L(){e=u(t.opts,["#22d3ee","#7c5cff"]),a=_(t,e),o=i(t.opts.radiusRatio,.02,.35),r=i(t.opts.limbDarkening,0,.95),l=i(t.opts.impact,0,.95);var s=86400*(c=Math.max(.2,t.opts.period));h=Math.pow(6674e-14*1408*s*s/(3*Math.PI),1/3),d=Math.sqrt(Math.max(.02,(1+o)*(1+o)-l*l))+.55,g=24*c/Math.PI*Math.asin(i(Math.sqrt((1+o)*(1+o)-l*l)/h,-1,1)),v=[];var f,P=1;for(f=0;f<=C;f++){var R=2*d*(f/C)-d,L=z(Math.sqrt(R*R+l*l));v.push(L),L=1&&(b-=1,y=[],S=-1);var E=Math.floor(b*C),F=2*d*b-d,q=Math.sqrt(F*F+l*l),D=v[i(E,0,C)];if(E>S){for(m=Math.max(0,S+1);m<=E;m++)y.push({i:m,f:v[m]+U()*p*.055});S=E}O(L,"TRANSIT PHOTOMETRY",14,15,a.label,.9),O(L,"synthetic · linear limb-darkening law",t.width-12,15,a.label,.4,"right");var B=k.r;L.save(),L.beginPath(),L.arc(k.cx,k.cy,B,0,n()),L.clip();var _=L.createRadialGradient(k.cx,k.cy,0,k.cx,k.cy,B);for(m=0;m<=8;m++){var W=m/8,V=.88*P(Math.min(W,.999));_.addColorStop(W,"rgb("+Math.round(w.r*V)+","+Math.round(w.g*V)+","+Math.round(w.b*V)+")")}L.fillStyle=_,L.beginPath(),L.arc(k.cx,k.cy,B,0,n()),L.fill();var H=.16*e*t.opts.speed;for(m=0;m0?{r:255,g:245,b:220}:{r:90,g:45,b:20};Z.addColorStop(0,s($,.2*J*Math.abs(N.v)*2.1)),Z.addColorStop(1,s($,0)),L.fillStyle=Z,L.save(),L.translate(X,Y),L.scale(Math.max(.16,J),1),L.translate(-X,-Y),L.beginPath(),L.arc(X,Y,Q,0,n()),L.fill(),L.restore()}}L.restore(),L.strokeStyle=s(f(w,a.bg,.45),.9),L.lineWidth=1,L.beginPath(),L.arc(k.cx,k.cy,B-.5,0,n()),L.stroke();var tt=k.cx+F*B,et=k.cy-l*B;L.fillStyle=s({r:4,g:6,b:12},1),L.beginPath(),L.arc(tt,et,o*B,0,n()),L.fill(),L.strokeStyle=s(a.hi,.7),L.lineWidth=1,L.beginPath(),L.arc(tt,et,o*B+.5,0,n()),L.stroke(),L.setLineDash([2,4]),I(L,k.cx-d*B,et,k.cx+d*B,et,a.hi,.26),L.setLineDash([]);var at=k.cx+B+30,ot=at+116,rt=k.cy-27;function it(t,e,o,r,i){O(L,o,t,rt+18*e,a.label,.45),O(L,r,t+62,rt+18*e,i?a.hi:a.label,i?.95:.85)}it(at,0,"Rp/Rs",o.toFixed(3)),it(at,1,"DEPTH",(100*p).toFixed(2)+" %",1),it(at,2,"b",l.toFixed(2)),it(at,3,"u",r.toFixed(2)),it(ot,0,"P",c.toFixed(2)+" d"),it(ot,1,"a/Rs",h.toFixed(2)),it(ot,2,"T14",g.toFixed(2)+" h");var nt=F/(n()*h)*c*24;it(ot,3,"t-t0",(nt>=0?"+":"")+nt.toFixed(2)+" h",1),G(L,T.x,T.y,T.w,T.h,a.axis,.45);var lt=[1,1-.5*p,1-p];for(m=0;m3?2:st>1.4?1:.5,ft=-Math.floor(st/ct)*ct;ft<=st+1e-6;ft+=ct){var ut=(ft/st+1)/2;x=T.x+ut*T.w,I(L,x,T.y,x,T.y+T.h,a.grid,Math.abs(ft)<1e-6?.7:.3);var ht=(ft>0?"+":"")+(ct<1?ft.toFixed(1):String(Math.round(ft)));O(L,ht,x,T.y+T.h+14,a.label,.55,"center")}for(O(L,"h from mid-transit",T.x+T.w,T.y+T.h+27,a.label,.42,"right"),L.strokeStyle=s(a.hi,.35),L.lineWidth=1,L.beginPath(),m=0;m<=C;m++)x=T.x+m/C*T.w,z=A(v[m]),0===m?L.moveTo(x,z):L.lineTo(x,z);for(L.stroke(),L.fillStyle=s(a.data,.85),m=0;m1-a&&(r=.5-.5*Math.cos(Math.PI*(1-e)/a)),.16+.84*r}function k(e){n[l]=function(e){var a,r,i=[],n=Math.max(.05,t.opts.noise);for(a=0;ao/2&&(d-=o),d<-o/2&&(d+=o),i[a]+=f.a*h*Math.exp(-Math.pow(d/1.15,2))}}for(r=g.length-1;r>=0;r--){var m=g[r],x=e-m.t0;if(!(x<0))if(x>m.dur)g.splice(r,1);else if(m.wide){var b=1-x/m.dur;for(a=0;a.94&&g.push({t0:C,dur:W(5.1*S)>.72?.16:.62,a:.7,wide:W(5.1*S)>.72}),k(C)}T()}return P(),{resize:P,update:P,draw:function(e,i){R(t);var n,l,f,u=t.ctx,h=t.opts.speed;x+=i*h;for(var d=0;x>=m&&d<8;){if(x-=m,b+=m,d++,Math.random()<.012){var v=Math.random()>.72;g.push({t0:b+m,dur:v?.1+.12*Math.random():.45+.45*Math.random(),a:.62+.3*Math.random(),wide:v})}k(b)}d&&T(),O(u,"RADIO SPECTROGRAM",y.x,15,a.label,.9),O(u,o+" ch · "+8..toFixed(0)+" MHz · HI 1420",t.width-12,15,a.label,.45,"right"),u.save(),u.imageSmoothingEnabled=!1,u.drawImage(c,0,0,o,r,Math.round(y.x),Math.round(y.y),Math.round(y.w),Math.round(y.h)),u.restore(),G(u,y.x,y.y,y.w,y.h,a.axis,.55);for(var p=w-4,C=w+4,P=2*Math.ceil(p/2);P<=C;P+=2)I(u,l=y.x+(P-p)/8*y.w,y.y+y.h,l,y.y+y.h+4,a.axis,.85),O(u,String(Math.round(P)),l,y.y+y.h+15,a.label,.6,"center");O(u,"MHz",y.x+y.w,y.y+y.h+27,a.label,.45,"right");for(var z=r*m,L=z>8?4:z>4?2:1,A=0;A<=z+1e-6;A+=L)f=y.y+y.h-A/z*y.h,I(u,y.x-4,f,y.x,f,a.axis,.8),O(u,0===A?"now":"-"+A.toFixed(0)+"s",y.x-7,f+3.5,a.label,.55,"right");for(n=0;n<40;n++){var E=S(1-n/39);u.fillStyle=s(E,1),u.fillRect(M.x,y.y+n/40*y.h,M.w,y.h/40+1)}G(u,M.x,y.y,M.w,y.h,a.axis,.55);var F=[12,6,0];for(n=0;n0?"+":" ")+F[n],M.x+M.w+4,f+(0===n?8:n===F.length-1?0:3.5),a.label,.55);O(u,"dB",M.x,y.y+y.h+15,a.label,.5)}}}}),k("hrDiagram",{defaults:{count:150,evolve:1,speed:1,interactive:!0,colors:["#22d3ee","#7c5cff"],background:"#04060f"},setup:function(t){var e,a,o,l,c={x:0,y:0,w:0,h:0},f=Math.log(5772)/Math.LN10,h=[{c:"O",lo:3e4,hi:52e3},{c:"B",lo:1e4,hi:3e4},{c:"A",lo:7500,hi:1e4},{c:"F",lo:6e3,hi:7500},{c:"G",lo:5200,hi:6e3},{c:"K",lo:3700,hi:5200},{c:"M",lo:2300,hi:3700}];function d(t){return c.x+(1-(t-3.36)/(4.66-3.36))*c.w}function v(t){return c.y+c.h-(t- -4.8)/11.1*c.h}var p=.6,g=.645,m=.775,x=.8,b=.83;function y(t,e){var a,o,i,n,l=t.z;return t.m<=.8&&(e=(e%p+p)%p),e=.5?3.8*a:3.8*Math.log(.5)/Math.LN10+2.3*(a-Math.log(.5)/Math.LN10),lT:f+.25*e-.5*(t>=1?.6*a:.9*a)}}(a),l=Math.max(o.lL+.4,Math.min(3.55,o.lL+2.45))+.5*(W(13.7*t)-.5),s=3.7-.025*i(l,0,5.5)+.055*(W(19.1*t)-.5),c={m:a,z:o,tipL:l,tipT:s,tSub:s+.1,p:W(5.13*t),rate:.024*Math.pow(a,-.3),twp:W(8.9*t)*n(),tf:.5+1.1*W(3.3*t),tw:1,col:null,rr:1,sx:0,sy:0,lL:0,lT:0,ph:0,R:1};return c.m<=.8&&(c.p=W(5.13*t)*p),c}function w(){e=u(t.opts,["#22d3ee","#7c5cff"]),a=_(t,e),c.x=42,c.y=38,c.w=t.width-c.x-14,c.h=Math.max(60,t.height-c.y-58),o=[];for(var r=Math.max(20,Math.min(400,Math.round(t.opts.count))),i=0;i0?"+":"")+w,c.x-6,m+3.5,a.label,.55,"right");var S=[3e4,2e4,1e4,7e3,5e3,4e3,3e3];for(u=0;uc.x+c.w||(I(b,g,c.y,g,c.y+c.h,a.grid,.3),I(b,g,c.y+c.h,g,c.y+c.h+4,a.axis,.8),O(b,S[u]>=1e4?S[u]/1e3+"k":String(S[u]),g,c.y+c.h+15,a.label,.55,"center"));for(u=0;uc.x+c.w-5)){O(b,h[u].c,g,c.y-7,a.label,.7,"center");var k=d(Math.log(h[u].lo)/Math.LN10);k>c.x&&k cooler",c.x+c.w,c.y+c.h+28,a.label,.42,"right"),O(b,"GIANTS",d(3.56),v(3.15),a.label,.38),O(b,"MAIN SEQUENCE",d(3.96),v(-.95),a.label,.38),O(b,"WHITE DWARFS",d(4.5),v(-3.15),a.label,.38);var T=t.mouse.x,P=t.mouse.y,z=null,L=144;for(u=0;u=p&&(x.p-=p):x.p>=1&&(x.p-=1);var A=y(x,x.p);if(x.lL=A.lL,x.lT=A.lT,x.ph=A.ph,x.sx=d(A.lT),x.sy=v(A.lL),x.R=Math.pow(10,.5*A.lL-2*(A.lT-f)),!1!==t.opts.interactive&&t.mouse.active){var E=x.sx-T,F=x.sy-P,q=E*E+F*F;qc.x+c.w+4||x.syc.y+c.h+4||(x.col=V(Math.pow(10,x.lT)),x.rr=i(1.4+Math.log(x.R)/Math.LN10*.8,1,3.3),x.tw=.8+.2*Math.sin(e*x.tf*t.opts.speed+x.twp),_.push(x));for(b.globalCompositeOperation="lighter",u=0;u<_.length;u++){var W=3+1.5*(x=_[u]).rr,U=b.createRadialGradient(x.sx,x.sy,0,x.sx,x.sy,W);U.addColorStop(0,s(x.col,.3*x.tw)),U.addColorStop(1,s(x.col,0)),b.fillStyle=U,b.beginPath(),b.arc(x.sx,x.sy,W,0,n()),b.fill()}for(b.globalCompositeOperation="source-over",u=0;u<_.length;u++)x=_[u],b.fillStyle=s(x.col,.55+.45*x.tw),b.beginPath(),b.arc(x.sx,x.sy,x.rr,0,n()),b.fill();if(l){x=l;var H=10*Math.round(Math.pow(10,x.lT)/10),N=function(t){for(var e=0;e=h[e].lo)return h[e].c;return"M"}(H)+" "+(D=x.ph,B=x.lL,(4===D?"D":3===D?"pAGB":2===D?B>4?"I":"III":1===D?"IV":"V")+" ")+H+" K",j="log L "+(x.lL>=0?"+":"")+x.lL.toFixed(2),K="M "+x.m.toFixed(2)+" R "+(x.R<10?x.R.toFixed(2):x.R.toFixed(0)),J=6.02*Math.max(N.length,j.length,K.length)+16,X=i(x.sx+12,c.x+2,c.x+c.w-J-2),Y=i(x.sy-46-10,c.y+2,c.y+c.h-46-2);b.strokeStyle=s(a.hi,.9),b.lineWidth=1,b.beginPath(),b.arc(x.sx,x.sy,7,0,n()),b.stroke(),b.fillStyle=s(a.bg,.94),b.fillRect(Math.round(X),Math.round(Y),Math.round(J),46),G(b,X,Y,J,46,a.hi,.75),O(b,N,X+8,Y+15,a.hi,.95),O(b,j,X+8,Y+28,a.label,.8),O(b,K,X+8,Y+41,a.label,.8)}}}}}),k("pulsarTiming",{defaults:{period:5.757451924,jitter:1.2,folds:600,speed:1,colors:["#22d3ee","#7c5cff"],background:"#04060f"},setup:function(t){var e,a,o,r,l,c,f,h,d,v,p,g,m,x=128,b={x:0,y:0,w:0,h:0},y={x:0,y:0,w:0,h:0},M=78;function w(t){var e=0;return e+=1*Math.exp(-Math.pow((t-.32)/.021,2)),(e+=.33*Math.exp(-Math.pow((t-.372)/.038,2)))+.21*Math.exp(-Math.pow((t-.79)/.027,2))}function S(t){return 2.4*Math.sin(n()*t/331)+.85*Math.sin(n()*t/47+1.1)}var C=!0;function k(){var t;for(t=0;tm&&(m=n)}var s=Math.max(12,Math.round(.28*Math.max(40,t.opts.folds)));for(i=0;i14?10:p>7?5:p>3.5?2:1,f=new Array(M),h=0,v=0,d=0,i=0;i=A){for(var F=0;F=.09&&(c=0,C=!0),d+=u*L;d>.42;)d-=.42,T(v+=5.4);var q=t.opts.period;O(z,"PSR J0437-4715",b.x,14,a.label,.9),O(z,"P "+q.toFixed(9)+" ms · DM 2.645",t.width-14,14,a.label,.55,"right");var D=b.y+b.h,B=b.h/(1.62*m);function _(t){return D-(t+.36*m)*B}G(z,b.x,b.y,b.w,b.h,a.axis,.45),I(z,b.x,_(0),b.x+b.w,_(0),a.grid,.8);for(var W=0;W<=1.0001;W+=.25)I(z,S=b.x+W*b.w,b.y,S,D,a.grid,.28),I(z,S,D,S,D+4,a.axis,.8),O(z,W.toFixed(2),S,D+15,a.label,.55,"center");for(O(z,"PULSE PHASE",b.x+b.w,D+29,a.label,.42,"right"),O(z,"flux",b.x-6,b.y+10,a.label,.5,"right"),z.fillStyle=s(a.hi,.32),z.fillRect(b.x,D+22,10,8),O(z,"one rotation",b.x+14,D+29,a.label,.5),I(z,b.x+104,D+26,b.x+114,D+26,a.data,.95,1.4),O(z,"folded mean",b.x+118,D+29,a.label,.5),z.fillStyle=s(a.hi,.13),z.beginPath(),z.moveTo(b.x,_(0)),w=0;w.02&&H<.24){var N=o[w]/Math.max(1,r);U+=N*N,V++}}function j(t){return y.y+y.h/2-t/p*(y.h/2-4)}U=Math.sqrt(U/Math.max(1,V)),O(z,"FOLDED PROFILE · "+r+" / "+A+" rotations",b.x,b.y-7,a.hi,.9),O(z,"S/N "+(m/Math.max(.001,U)).toFixed(1),b.x+b.w,b.y-7,a.label,.7,"right"),G(z,y.x,y.y,y.w,y.h,a.axis,.45),z.setLineDash([3,3]),I(z,y.x,j(0),y.x+y.w,j(0),a.grid,.9),z.setLineDash([]);for(var K=-Math.floor(p/g)*g;K<=p;K+=g)0!==K&&((P=j(K))y.y+y.h-3||(I(z,y.x,P,y.x+y.w,P,a.grid,.28),O(z,(K>0?"+":"")+K,y.x-6,P+3.5,a.label,.5,"right")));O(z,"TIMING RESIDUALS (us)",y.x,y.y-6,a.label,.6);var J=0,X=f[h].d;for(f[(h+M-1)%M].d,w=0;w=a-1?t[a-1]:f(t[o],t[o+1],e-o)}function a(t){return(t=i(t,0,1))*t*(3-2*t)}function c(t,e){return 1-Math.exp(-t*e)}var h={r:255,g:255,b:255};k("gravityWell",{defaults:{count:700,mass:1,softening:.09,trail:.15,speed:1,colors:["#12266b","#2563eb","#22d3ee","#a7f3d0","#fde68a","#fb7185"],background:"#04050e",interactive:!0},setup:function(t){var a,r,l,c,d,v,p,g=[],m=!0,x=0,b=0,y=0,M=0,w=0,S=0,C=[];function k(t,e){var a=x-t,o=b-e,r=a*a+o*o+d*d,i=l/(r*Math.sqrt(r));w=a*i,S=o*i}function T(t){return t*c/Math.pow(t*t+d*d,.75)}function z(t){var e=r*(.32+.98*Math.pow(Math.random(),.72)),a=i(.24+o(-.07,.07),.02,.45),s=e/r*2.6+o(-.09,.09),c=Math.random()*n(),f=e*(1-a*a),u=f/(1+a*Math.cos(c)),h=s+c,d=Math.sqrt(l/f),v=d*a*Math.sin(c),p=d*(1+a*Math.cos(c)),g=Math.cos(h),m=Math.sin(h);t.x=x+u*g,t.y=b+u*m,t.vx=v*g-p*m,t.vy=v*m+p*g,t.px=t.x-.05*t.vx,t.py=t.y-.05*t.vy,t.r=o(.45,1.5),k(t.x,t.y),t.ax=w,t.ay=S}function L(){a=u(t.opts,["#12266b","#2563eb","#22d3ee","#fde68a","#fb7185"]),r=.34*Math.min(t.width,t.height),x=t.width/2,b=t.height/2,y=0,M=0,d=Math.max(2.5,(t.opts.softening||.09)*r),l=4*(t.opts.mass||1)*Math.PI*Math.PI*r*r*r/33.64,c=Math.sqrt(l),v=T(1.7*r),p=T(.3*r)-v;var o=Math.max(300,Math.round(t.opts.count*t.width*t.height/921600));g=[];for(var i=0;ir*r*12)z(u);else{var I=Math.sqrt(u.vx*u.vx+u.vy*u.vy),G=Math.pow(i((I-v)/p,0,1),.8);T=Math.min(11,12*G|0);var _=C[T];_.seg.push(u.px,u.py,u.x,u.y),G>.34&&_.dot.push(u.x,u.y,u.r)}}for(A.globalCompositeOperation="lighter",T=0;T<12;T++){var W=C[T],U=T/11;if(W.seg.length){for(A.strokeStyle=s(W.col,.17+.5*U),A.lineWidth=.6+1*U,A.beginPath(),L=0;La-1.002&&(e=a-1.002),o<0?o=0:o>r-1.002&&(o=r-1.002);var i=0|e,n=0|o,l=e-i,s=o-n,c=n*a+i,f=c+a;return(t[c]*(1-l)+t[c+1]*l)*(1-s)+(t[f]*(1-l)+t[f+1]*l)*s}var E=[0,0];function F(e,a,o){var r=e*h,i=a*h,n=7.4*(null==t.opts.swirl?1:t.opts.swirl),l=r+.3*o,s=i-.23*o,c=1.9*r-.17*o,f=1.7*i+.21*o,u=3.7*r+.13*o,d=3.2*i-.15*o,v=-Math.sin(l)*Math.sin(s)-.94*Math.sin(c)*Math.sin(f)-.9*Math.sin(u)*Math.sin(d),p=Math.cos(l)*Math.cos(s)+1.05*Math.cos(c)*Math.cos(f)+1.04*Math.cos(u)*Math.cos(d);E[0]=v*n,E[1]=-p*n}function q(t,e,o,i,n,l){var s,c,f=o*o,u=Math.max(0,Math.floor(t-2.3*o)),h=Math.min(a-1,Math.ceil(t+2.3*o)),m=Math.max(0,Math.floor(e-2.3*o)),x=Math.min(r-1,Math.ceil(e+2.3*o));for(c=m;c<=x;c++)for(s=u;s<=h;s++){var b=s-t,y=c-e,M=(b*b+y*y)/f;if(!(M>5.3)){var w=Math.exp(-M),S=c*a+s;p[S]+=i*w,g[S]+=(n-g[S])*Math.min(.9,.8*w),l&&(d[S]+=-y*l*w,v[S]+=b*l*w)}}}function D(e,o){var n,l,s,f=c(o,1.15);for(l=0;l.001){for(var D=1;D5e-4?1/b:0,I=i(E*B,-46,46),G=i(F*B,-46,46),_=((L+=(.03*q+.1)*Math.max(b,.008))%1+1)%1,W=.03*a+1.2+Math.min(3,.4*q),U=Math.min(1,9*b),V=W*W,H=Math.max(0,Math.floor(y-2.3*W)),N=Math.min(a-1,Math.ceil(y+2.3*W)),j=Math.max(0,Math.floor(A-2.3*W)),K=Math.min(r-1,Math.ceil(A+2.3*W));for(h=j;h<=K;h++)for(u=H;u<=N;u++){var J=u-y,X=h-A,Y=(J*J+X*X)/V;if(!(Y>5.3)){var Q=Math.exp(-Y);p[m=h*a+u]+=Q*(1.1+Math.min(3.4,1.1*q))*Math.max(b,.006)*4.5,g[m]+=(_-g[m])*Q*.5,d[m]+=(I-d[m])*Q*U+-X*Q*U*2.2,v[m]+=(G-v[m])*Q*U+J*Q*U*2.2}}T=y,P=A}else z=!1;b>0&&(O(o,b),D(o,b));var Z=S.data;for(m=0;m1.06*_&&(s.out=!0),s.shock>0&&(s.shock-=2.4*e),I>.02&&D<1.3*G&&D>.55*G){var W=(z*P+F*R)/D,U=Math.abs(W);if(U>.34&&U<.95&&Math.random()<22*e){var V=W>0;!V&&Math.random()<.62&&(V=!0);var H=z/D*v.x+F/D*v.y,N=z/D*p.x+F/D*p.y,j=Math.atan2(N,H),K=L(V?1:-1,j,w);s.st=1,s.fp=0,s.pole=V?1:-1,s.f0x=s.x,s.f0y=s.y,s.tgx=K.x,s.tgy=K.y;var J=.5*(s.x+K.x)-r,X=.5*(s.y+K.y)-l;s.c1x=r+2.15*J,s.c1y=l+2.15*X,s.bin=(Math.floor((j+Math.PI)/n()*S)%S+S)%S;continue}}var Y=B*B+O*O,Q=G*G/(Y*Y),Z=1/(1+Math.max(0,B)/(2*G)),$=c*(1-Q*(B*B-O*O)*Z),tt=c*(2*-Q*B*O*Z),et=$*x+tt*u,at=$*b+tt*m,ot=Math.min(1,7*e);s.vx+=(et-s.vx)*ot,s.vy+=(at-s.vy)*ot,s.out||(s.vx+=o(-1,1)*c*.1,s.vy+=o(-1,1)*c*.1),s.x+=s.vx*e,s.y+=s.vy*e,z=s.x-r,F=s.y-l,(D=Math.sqrt(z*z+F*F)||.001)t.width+rt||s.y<-rt||s.y>t.height+rt)&&E(s,!1)}else{s.fp+=2*e*(t.opts.speed||1);var it=Math.min(1,s.fp),nt=1-it;if(s.x=nt*nt*s.f0x+2*nt*it*s.c1x+it*it*s.tgx,s.y=nt*nt*s.f0y+2*nt*it*s.c1y+it*it*s.tgy,s.fp>=1){var lt=s.pole>0?C:k;lt[s.bin]+=2.2,lt[(s.bin+1)%S]+=.9,lt[(s.bin+S-1)%S]+=.9,E(s,!1)}}q(C,e),q(k,e)}function B(t,o,r,l,c,f,u){var h,d=null;for(h=0;h<=S;h++){var v=h%S,p=L(o,v/S*n(),l),g=p.x,m=p.y,x=i(5*(p.z+.04),0,1),b=r[v]*f*x;d&&(x>.01||d.v>.01)&&(t.strokeStyle=s(e(a,i(.03+.22*b,0,1)),i(b*u,0,.9)),t.lineWidth=c*(.55+.42*i(b,0,3)),t.beginPath(),t.moveTo(d.x,d.y),t.lineTo(g,m),t.stroke()),d={x:g,y:m,v:x}}}function O(t,e,a,o){var r;for(t.beginPath(),r=0;r0&&D(p),v.globalCompositeOperation="lighter";for(var G=-b,_=x,W=0;W<2;W++){for(v.beginPath(),d=0;d<=90;d++){var U=-Math.PI+d/90*n(),V=Math.cos(U),H=Math.sin(U),N=A(V)*(W?1.28:1)+(W?.24*f:0),j=r+(-x*V+G*H)*N,K=l+(-b*V+_*H)*N;0===d?v.moveTo(j,K):v.lineTo(j,K)}v.strokeStyle=s(a[W?3:1]||a[0],W?.2:.3),v.lineWidth=W?1.3:1,v.stroke()}var J=[[],[],[]];for(u=0;u0?1:0].push(h.px,h.py,h.x,h.y);for(var X=[a[1]||a[0],a[3]||a[0],a[0]],Y=[.72,.86,1],Q=[1.5,1.9,2.5],Z=0;Z<3;Z++){var $=J[Z];if($.length){for(v.strokeStyle=s(X[Z],Y[Z]),v.lineWidth=Q[Z],v.beginPath(),u=0;u<$.length;u+=4)v.moveTo($[u],$[u+1]),v.lineTo($[u+2],$[u+3]);v.stroke()}}v.globalCompositeOperation="source-over",v.fillStyle="#060911",v.beginPath(),v.arc(r,l,f,0,n()),v.fill();var tt=v.createRadialGradient(r-x*f*.7,l-b*f*.7,.1*f,r-x*f*.2,l-b*f*.2,1.5*f);tt.addColorStop(0,s(a[1]||a[0],.17)),tt.addColorStop(.45,s(a[2]||a[0],.05)),tt.addColorStop(1,"rgba(0,0,0,0)"),v.fillStyle=tt,v.beginPath(),v.arc(r,l,f,0,n()),v.fill(),v.globalCompositeOperation="lighter";var et=v.createRadialGradient(r,l,.93*f,r,l,1.26*f);et.addColorStop(0,s(a[1]||a[0],.26)),et.addColorStop(1,"rgba(0,0,0,0)"),v.fillStyle=et,v.beginPath(),v.arc(r,l,1.26*f,0,n()),v.fill(),B(v,1,C,w,8,1,.1),B(v,1,C,w,3,1,.3),B(v,1,C,w,1.1,1,.6),B(v,-1,k,w,6,.85,.1),B(v,-1,k,w,2.2,.85,.34),v.strokeStyle=s(a[0],.22),v.lineWidth=1,O(v,1,C,w),v.strokeStyle=s(a[0],.14),O(v,-1,k,w),v.globalCompositeOperation="source-over"}}}}),k("starForge",{defaults:{maxSystems:4,formation:1,speed:1,colors:["#fde68a","#22d3ee","#a78bfa","#fb7185"],background:"#03040c",interactive:!0},setup:function(t){var e,l=[],c=[],d=[],v=!0,p=0,g=0,m=0,x=!1,b=5,y=1,M=[{c:{r:255,g:142,b:92},s:.7},{c:{r:255,g:194,b:116},s:.86},{c:{r:255,g:240,b:206},s:1},{c:{r:210,g:228,b:255},s:1.22},{c:{r:172,g:204,b:255},s:1.42}];function w(t){return t<.3?f({r:182,g:140,b:108},{r:228,g:176,b:118},Math.random()):t<.58?f({r:104,g:170,b:150},{r:118,g:184,b:228},Math.random()):f({r:148,g:200,b:234},{r:208,g:226,b:248},Math.random())}function S(e,a,r){var i,l=o(.6,1)*Math.min(t.width,t.height)*.3,s={x:e,y:a,age:r,life:o(20,32),disk:l,incl:o(.18,.6),rot:Math.random()*Math.PI,spin:Math.random()<.5?1:-1,star:M[Math.random()*M.length|0],motes:[],planets:[],vx:o(-3,3),vy:o(-2.2,2.2)};for(i=0;i<78;i++){var c=l*o(.26,1.14);s.motes.push({a:Math.random()*n(),r0:l*o(1.7,3.6),rt:c,w:s.spin*(.95/Math.pow(c/l,1.5))*o(.85,1.15),br:o(.3,1.05)})}var f=3+(4*Math.random()|0),u=l*o(.26,.34);for(i=0;i1.18*l)break;var d=h/(1.18*l);s.planets.push({r:h,a:Math.random()*n(),w:s.spin*(1.05/Math.pow(h/l,1.5))*.55,sz:(1+2*Math.random())*(.5+d)*y,col:w(d),ring:d>.5&&Math.random()<.42,born:2.6+.46*i})}return s}function C(){e=u(t.opts,["#fde68a","#22d3ee","#a78bfa","#fb7185"]),y=Math.min(t.width,t.height)/300,d=[];for(var a=Math.round(t.width*t.height/3600),r=0;rt.width+6&&(B.x-=t.width+12),B.y<-6&&(B.y+=t.height+12),B.y>t.height+6&&(B.y-=t.height+12),T.moveTo(B.x+B.r,B.y),T.arc(B.x,B.y,B.r,0,n())}if(T.fillStyle=s({r:168,g:186,b:230},.3),T.fill(),E){(p+=z*(.3+Math.min(1.5,.004*D)))>=1&&l.length<(t.opts.maxSystems||4)&&(l.push(S(F,q,0)),p=0);var U=4+24*p,V=T.createRadialGradient(F,q,0,F,q,U);V.addColorStop(0,s(e[0],.5+.4*p)),V.addColorStop(.4,s(e[2]||e[0],.18)),V.addColorStop(1,"rgba(0,0,0,0)"),T.fillStyle=V,T.beginPath(),T.arc(F,q,U,0,n()),T.fill(),T.strokeStyle=s(e[0],.2+.45*p),T.lineWidth=1,T.beginPath(),T.arc(F,q,.55*U+3,-Math.PI/2,-Math.PI/2+n()*i(p,.02,1)),T.stroke(),g=F,m=q}else p>0&&(p=Math.max(0,p-.5*z));if((b-=z)<=0&&(b=o(7,13),l.length<(t.opts.maxSystems||4))){var H=function(){for(var e=0,a=0,r=-1,i=0;i<14;i++){for(var n=o(.15*t.width,.85*t.width),s=o(.18*t.height,.82*t.height),c=1e9,f=0;fr&&(r=c,e=n,a=s)}return{x:e,y:a}}();l.push(S(H.x,H.y,0))}for(w=l.length-1;w>=0;w--)if((k=l[w]).age+=z*L,k.x+=k.vx*z,k.y+=k.vy*z,k.age>k.life)l.splice(w,1);else{var N=a(k.age/.3)*a((k.life-k.age)/4.5),j=Math.cos(k.rot),K=Math.sin(k.rot),J=a(k.age/1),X=i((k.age-1)/.32,0,1),Y=N*(k.age<6?a(k.age/1.3):Math.max(.1,1-(k.age-6)/6));if(Y>.01&&k.age>.55){T.save(),T.translate(k.x,k.y),T.rotate(k.rot),T.scale(1,k.incl);var Q=1.2*k.disk,Z=T.createRadialGradient(0,0,.14*k.disk,0,0,Q);Z.addColorStop(0,s(f(k.star.c,e[1]||e[0],.45),.2*Y)),Z.addColorStop(.42,s(f(e[2]||e[0],k.star.c,.35),.11*Y)),Z.addColorStop(1,"rgba(0,0,0,0)"),T.fillStyle=Z,T.beginPath(),T.arc(0,0,Q,0,n()),T.fill(),T.restore()}for(T.beginPath(),C=0;Cot.born){var rt=.08*k.disk*i((k.age-ot.born)/1.4,0,1),it=Math.abs(tt-ot.r);it1&&k.age<2.6){var dt=(k.age-1)/1.6,vt=1-dt,pt=k.disk*(.18+2.3*dt);T.strokeStyle=s({r:255,g:248,b:230},.55*vt*vt*N),T.lineWidth=2.6*vt+.4,T.beginPath(),T.ellipse(k.x,k.y,pt,pt*r(1,k.incl,.5),k.rot,0,n()),T.stroke();var gt=1.7*k.disk*Math.sin(Math.PI*i(1.15*dt,0,1)),mt=-K,xt=j,bt=T.createLinearGradient(k.x-mt*gt,k.y-xt*gt,k.x+mt*gt,k.y+xt*gt);bt.addColorStop(0,"rgba(0,0,0,0)"),bt.addColorStop(.5,s(e[1]||e[0],.4*N*vt)),bt.addColorStop(1,"rgba(0,0,0,0)"),T.strokeStyle=bt,T.lineWidth=2.4*y*vt+.6,T.beginPath(),T.moveTo(k.x-mt*gt,k.y-xt*gt),T.lineTo(k.x+mt*gt,k.y+xt*gt),T.stroke()}for(C=0;C.002){var At=(1.9+3*k.star.s)*y*(.55+.45*X),Et=1+.7*Math.exp(3.4*-Math.max(0,k.age-1)),Ft=5.5*At*Et,qt=T.createRadialGradient(k.x,k.y,0,k.x,k.y,Ft);qt.addColorStop(0,s(k.star.c,.6*Lt)),qt.addColorStop(.22,s(k.star.c,.14*Lt)),qt.addColorStop(1,"rgba(0,0,0,0)"),T.fillStyle=qt,T.beginPath(),T.arc(k.x,k.y,Ft,0,n()),T.fill(),T.fillStyle=s(f(k.star.c,h,.55),.95*Lt),T.beginPath(),T.arc(k.x,k.y,At*Et,0,n()),T.fill()}}T.globalCompositeOperation="source-over"}}}}),m("relativisticJets",{defaults:{beta:.96,tilt:.75,knots:6,speed:1,colors:["#dbeafe","#7c5cff","#ffb168"],background:"#03040d",interactive:!0},staticTime:9,uniforms:function(e,a){var o=a.__rjets;o||(o=a.__rjets={cx:Math.cos(e.tilt||0),sy:Math.sin(e.tilt||0),psi:.35,lt:a.t});var r=i(a.t-o.lt,0,.06);o.lt=a.t;var n=(e.tilt||0)+.52*Math.sin(.085*a.t),s=.6*Math.sin(.055*a.t+.7);if(a.mouse.active&&a.width>2){var f=a.mouse.x/a.width*2-1,u=-(a.mouse.y/a.height*2-1);n=Math.atan2(u,f),s=1.02*(1-Math.min(1,Math.sqrt(f*f+u*u)/.92))}var h=c(r,5.5);o.cx+=(Math.cos(n)-o.cx)*h,o.sy+=(Math.sin(n)-o.sy)*h;var d=Math.sqrt(o.cx*o.cx+o.sy*o.sy)||1;o.cx/=d,o.sy/=d,o.psi+=(s-o.psi)*c(r,3.4);var v=Math.cos(o.psi),p=e.colors&&e.colors.length?e.colors:["#dbeafe","#7c5cff","#ffb168"],g=l(e.background||"#03040d");return{uSpeed:null==e.speed?1:e.speed,uBeta:i(null==e.beta?.96:e.beta,0,.998),uKnots:i(null==e.knots?6:e.knots,1,8),uAxis:[o.cx*v,o.sy*v,Math.sin(o.psi)],uTint:t(p[0]),uSheath:t(p[1]||p[0]),uTorus:t(p[2]||p[0]),uBg:[g.r/255,g.g/255,g.b/255]}},fragment:["float h11(float n){ return fract(sin(n*127.1)*43758.5453123); }","float h21(vec2 p){ return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453123); }","float sq(float x){ return x*x; }","float starfield(vec2 p){"," vec2 g=floor(p*40.0); float r=h21(g);"," vec2 c=(g+vec2(h21(g+1.3),h21(g+7.1)))/40.0;"," return smoothstep(0.0038,0.0,length(p-c))*step(0.955,r)*(0.3+0.7*h21(g+3.7));","}","void main(){"," float mn = min(uResolution.x, uResolution.y);"," vec2 p = (gl_FragCoord.xy - 0.5*uResolution)/mn;"," float t = uTime*uSpeed;"," vec3 n = normalize(uAxis);"," float L = max(length(n.xy), 1e-4);"," vec2 ax = n.xy/L;"," vec2 pr = vec2(-ax.y, ax.x);"," float s = dot(p, ax);"," float q = dot(p, pr);"," float Lc = max(L, 0.16);"," float rr = length(p);"," float b2 = clamp(uBeta, 0.0, 0.998);"," float gam = 1.0/sqrt(max(1e-4, 1.0-b2*b2));"," float dA = 1.0/max(1e-3, gam*(1.0-b2*n.z));"," float dB = 1.0/max(1e-3, gam*(1.0+b2*n.z));"," float pA = pow(max(dA,1e-3), 3.4);"," float pB = pow(max(dB,1e-3), 3.4);"," float pm = max(pA, pB);"," float bA = max(pA/pm, 0.035);"," float bB = max(pB/pm, 0.035);"," vec3 col = vec3(0.0);"," for (int j = 0; j < 2; j++) {"," float sg = (j==0) ? 1.0 : -1.0;"," float bo = (j==0) ? bA : bB;"," float df = (j==0) ? dA : dB;"," float sd = s*sg;"," float inj = step(0.0008, sd);"," float d3 = max(sd,0.0)/Lc;"," float emerge = smoothstep(0.03, 0.22, d3);"," float w = 0.0085 + d3*0.058;"," float core = exp(-sq(q/w));"," float sheath = exp(-sq(q/(w*2.6)));"," float cocoon = exp(-sq(q/(w*6.5)));"," float reach = smoothstep(1.28, 0.06, d3);"," float atten = 1.0/(0.55 + d3*1.5);"," float base = reach*atten*inj*emerge;"," float kn = 0.0;"," float rate = min(df, 3.0);"," for (int i = 0; i < 8; i++) {"," float fi = float(i);"," float m = step(fi+0.5, uKnots);"," float ph = fract(t*0.17*rate + fi/max(uKnots,1.0) + sg*0.37 + h11(fi+sg*11.0)*0.22);"," float kp = ph*1.24;"," float kw = 0.022 + kp*0.05;"," kn += m*exp(-sq((d3-kp)/kw))*(1.0-ph*0.72);"," }"," float lobe = exp(-sq((d3-1.1)/0.24))*exp(-sq(q/(w*1.6)))*inj;"," col += uTint*(core*0.6 + kn*core*1.55)*base*bo;"," col += uSheath*(sheath*0.3 + cocoon*0.12)*base*bo*1.4;"," col += uTint*lobe*bo*0.45;"," }"," float eu = dot(p, pr)/0.135;"," float ev = dot(p, ax)/(0.135*max(abs(n.z),0.15));"," float er = sqrt(eu*eu + ev*ev);"," float ring = exp(-sq((er-1.0)*2.6));"," float cphi = (er>1e-4) ? eu/er : 0.0;"," float rb = clamp(0.6/max(0.22, 1.0-0.6*cphi*L), 0.32, 2.2);"," float turb = 0.78 + 0.34*sin(atan(ev, eu+1e-5)*5.0 - t*2.0);"," float torus = (ring*rb*turb + exp(-er*er*2.2)*0.16)*smoothstep(0.030,0.056,rr);"," col += uTorus*torus*0.7;"," col += vec3(1.0,0.94,0.84)*exp(-sq((rr-0.060)*46.0))*0.3;"," col += uSheath*exp(-rr*rr*72.0)*0.32;"," col += vec3(0.8,0.87,1.0)*starfield(p)*0.42;"," col += uSheath*0.035*exp(-rr*1.8) + uBg;"," col = vec3(1.0) - exp(-col*1.15);"," fragColor = vec4(col, 1.0);","}"].join("\n")})}(),y("eventHorizon",{defaults:{spin:1,tilt:.42,diskInner:2.6,diskOuter:9,steps:220,exposure:.85,colors:["#fff4e2","#ffb46b","#7c5cff"],background:"#03040a",interactive:!0},scene:function(t,e){var a=M(t,e);a.toneMapping=t.NoToneMapping;var o=new t.Scene,r=new t.OrthographicCamera(-1,1,1,-1,0,1),i=u(e.opts,["#fff4e2","#ffb46b","#7c5cff"]),n=function(e){var a=function(t){return Math.pow(t/255,2.2)};return new t.Vector3(a(e.r),a(e.g),a(e.b))},s={uRes:{value:new t.Vector2(1,1)},uTime:{value:0},uTilt:{value:e.opts.tilt},uInner:{value:e.opts.diskInner},uOuter:{value:e.opts.diskOuter},uSpin:{value:e.opts.spin},uSteps:{value:e.opts.steps},uExposure:{value:e.opts.exposure},uHot:{value:n(i[0])},uWarm:{value:n(i[1%i.length])},uCool:{value:n(i[2%i.length])},uBg:{value:n(l(e.opts.background||"#03040a"))}},c=new t.ShaderMaterial({uniforms:s,vertexShader:"void main(){ gl_Position = vec4(position.xy, 0.0, 1.0); }",fragmentShader:["precision highp float;","uniform vec2 uRes; uniform float uTime, uTilt, uInner, uOuter, uSpin, uSteps, uExposure;","uniform vec3 uHot, uWarm, uCool, uBg;","out vec4 fragColor;","float hash(vec3 p){ return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453); }","float noise(vec3 p){"," vec3 i = floor(p), f = fract(p); f = f*f*(3.0-2.0*f);"," float a = mix(mix(mix(hash(i), hash(i+vec3(1,0,0)), f.x), mix(hash(i+vec3(0,1,0)), hash(i+vec3(1,1,0)), f.x), f.y),"," mix(mix(hash(i+vec3(0,0,1)), hash(i+vec3(1,0,1)), f.x), mix(hash(i+vec3(0,1,1)), hash(i+vec3(1,1,1)), f.x), f.y), f.z);"," return a;","}","float fbm(vec3 p){ float s=0.0, a=0.5; for(int i=0;i<4;i++){ s+=a*noise(p); p*=2.03; a*=0.5; } return s; }","vec3 sky(vec3 d){"," vec3 c = uBg;"," float lon = atan(d.z, d.x);"," float lat = asin(clamp(d.y, -1.0, 1.0));"," vec2 sc = vec2(lon * 0.1591549, lat * 0.3183099) * 150.0;"," vec2 cell = floor(sc), frc = fract(sc);"," for (int oy = -1; oy <= 1; oy++) {"," for (int ox = -1; ox <= 1; ox++) {"," vec2 o = vec2(float(ox), float(oy));"," vec2 id = cell + o;"," float r1 = hash(vec3(id, 1.0));"," if (r1 < 0.90) continue;"," vec2 pos = o + vec2(hash(vec3(id, 7.0)), hash(vec3(id, 13.0)));"," float dist = length(frc - pos);"," float mag = (r1 - 0.90) * 10.0;"," float tw = 0.72 + 0.28 * sin(uTime * 1.9 + r1 * 90.0);"," vec3 tint = mix(vec3(0.62, 0.74, 1.0), vec3(1.0, 0.82, 0.62), hash(vec3(id, 21.0)));"," c += tint * pow(mag, 3.0) * tw * exp(-dist * dist * 190.0);"," }"," }"," float dy = d.y * 7.0;"," float band = exp(-dy * dy) * (0.012 + 0.05 * fbm(d * 7.0));"," c += uCool * band * 0.35;"," return c;","}","vec3 disk(float r, float phi){"," float x = clamp((r - uInner) / max(0.001, uOuter - uInner), 0.0, 1.0);"," float temp = pow(uInner / r, 0.75);"," float orbit = uTime * uSpin * 34.0 / pow(r, 1.5);"," float turb = fbm(vec3(cos(phi + orbit) * r, sin(phi + orbit) * r, r * 0.4) * 0.9);"," float dens = smoothstep(0.0, 0.09, x) * (1.0 - smoothstep(0.30, 0.95, x));"," dens *= 0.30 + 1.05 * turb * turb;"," vec3 col = mix(uWarm, uHot, clamp(temp * 1.25, 0.0, 1.0));"," return col * dens * (0.85 * temp * temp);","}","void main(){"," vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;"," float ct = cos(uTilt), st = sin(uTilt);"," vec3 eye = vec3(0.0, st, -ct) * 22.0;"," vec3 fwd = normalize(-eye);"," vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), fwd));"," vec3 up = cross(fwd, right);"," vec3 dir = normalize(fwd * 2.4 + right * uv.x + up * uv.y);"," vec3 nrm = normalize(cross(eye, dir));"," vec3 e1 = normalize(eye);"," vec3 e2 = normalize(cross(nrm, e1));"," float r = length(eye);"," float u = 1.0 / r;"," float dr = dot(dir, e1);"," float rdphi = dot(dir, e2);"," float du = -u * dr / max(1e-4, rdphi);"," float phi0 = 0.0;"," float dphi = 3.2 / uSteps;"," vec3 acc = vec3(0.0);"," bool captured = false;"," float prevY = dot(eye, vec3(0.0, 1.0, 0.0));"," vec3 pos = eye;"," for (int i = 0; i < 512; i++) {"," if (float(i) >= uSteps) break;"," float k1 = -u + 1.5 * u * u;"," float uMid = u + 0.5 * dphi * du;"," float duMid = du + 0.5 * dphi * k1;"," float k2 = -uMid + 1.5 * uMid * uMid;"," u += dphi * duMid;"," du += dphi * k2;"," phi0 += dphi;"," if (u <= 0.0) break;"," if (u > 1.0) { captured = true; break; }"," float rr = 1.0 / u;"," vec3 next = (e1 * cos(phi0) + e2 * sin(phi0)) * rr;"," float y = next.y;"," if (prevY * y < 0.0) {"," float f = prevY / (prevY - y);"," vec3 hit = mix(pos, next, f);"," float hr = length(hit.xz);"," if (hr > uInner && hr < uOuter) {"," float ph = atan(hit.z, hit.x);"," vec3 vdir = normalize(vec3(-hit.z, 0.0, hit.x));"," float beta = min(0.78, 1.02 / sqrt(max(1.2, hr)));"," vec3 look = normalize(hit - eye);"," float mu = dot(vdir, -look);"," float g = 1.0 / sqrt(1.0 - beta * beta);"," float delta = 1.0 / (g * (1.0 - beta * mu));"," float beam = pow(delta, 4.0);"," vec3 em = disk(hr, ph) * beam;"," em *= mix(vec3(1.0, 0.72, 0.5), vec3(0.72, 0.86, 1.0), clamp(delta * 0.5, 0.0, 1.0));"," acc += em;"," }"," }"," prevY = y; pos = next;"," }"," vec3 col = acc;"," if (!captured) {"," vec3 rhat = e1 * cos(phi0) + e2 * sin(phi0);"," vec3 phat = -e1 * sin(phi0) + e2 * cos(phi0);"," vec3 tangent = normalize(rhat * (-du / max(1e-6, u)) + phat);"," col += sky(tangent);"," }"," col *= uExposure;"," col = col / (1.0 + col);"," col = pow(col, vec3(1.0 / 2.2));"," fragColor = vec4(col, 1.0);","}"].join("\n"),glslVersion:t.GLSL3}),f=new t.Mesh(new t.PlaneGeometry(2,2),c);return f.frustumCulled=!1,o.add(f),{resize:function(t,o){a.setPixelRatio(e.dpr),a.setSize(t,o,!1),s.uRes.value.set(e.canvas.width,e.canvas.height)},update:function(t){s.uTilt.value=t.tilt,s.uInner.value=t.diskInner,s.uOuter.value=t.diskOuter,s.uSpin.value=t.spin,s.uExposure.value=t.exposure},draw:function(t){if(s.uTime.value=t,e.mouse.active){var i=e.mouse.y/Math.max(1,e.height)-.5;s.uTilt.value+=.06*(e.opts.tilt-.9*i-s.uTilt.value)}else s.uTilt.value+=.04*(e.opts.tilt-s.uTilt.value);a.render(o,r)},destroy:function(){w(o,[c,a])}}}}),y("molecularCloud",{defaults:{colors:["#ffb37a","#78aaff","#c58cff"],background:"#04060e",res:128,steps:64,density:9,shadow:2.6,emission:1.35,forward:.62,spin:.13,churn:.3,distance:2.02},scene:function(t,e){var a=M(t,e),o=e.opts,r=u(o,["#ffb37a","#78aaff","#c58cff"]);function i(t){return[Math.pow(t.r/255,2.2),Math.pow(t.g/255,2.2),Math.pow(t.b/255,2.2)]}function n(e){return new t.Vector3(e[0],e[1],e[2])}for(var s=i(l(o.background||"#04060e")),c=Math.max(48,Math.min(128,0|o.res)),f=new Float32Array(262144),h=1337,d=0;d>>0,f[d]=h/4294967296;function v(t,e,a,o,r){return f[((t&o)+r&63)<<12|((e&o)+3*r&63)<<6|(a&o)+7*r&63]}function p(t,e,a,o,r){var i=Math.floor(t),n=Math.floor(e),l=Math.floor(a),s=t-i,c=e-n,f=a-l;s=s*s*(3-2*s),c=c*c*(3-2*c),f=f*f*(3-2*f);var u=v(i,n,l,o,r),h=v(i+1,n,l,o,r),d=v(i,n+1,l,o,r),p=v(i+1,n+1,l,o,r),g=v(i,n,l+1,o,r),m=v(i+1,n,l+1,o,r),x=v(i,n+1,l+1,o,r),b=u+(h-u)*s,y=g+(m-g)*s,M=b+(d+(p-d)*s-b)*c;return M+(y+(x+(v(i+1,n+1,l+1,o,r)-x)*s-y)*c-M)*f}var g=24,m=new Float32Array(41472),x=.3;function b(t,e,a,o){return p(t,e,a,3,5*o+2)}for(var y=0;y1&&(X=1),X=X*X*(3-2*X);var tt=p(4*V,4*H,4*N,3,19);(tt=2.3*(tt-.3))<0&&(tt=0),tt>1&&(tt=1);var et=X*(.1+1.15*tt),at=2*V-1,ot=2*H-1,rt=2*N-1,it=(1-Math.sqrt(at*at+ot*ot*1.8+rt*rt)+.52*(.68*p(4*V+.5,4*H+.5,4*N+.5,3,41)+.32*p(8*V+.5,8*H+.5,8*N+.5,7,53)-.5))/.42;it<0&&(it=0),it>1&&(it=1),et*=it*it*(3-2*it);for(var nt=0;nt1?255:255*et|0}var ut=new t.Data3DTexture(O,c,c,c);ut.format=t.RedFormat,ut.type=t.UnsignedByteType,ut.minFilter=t.LinearFilter,ut.magFilter=t.LinearFilter,ut.wrapS=ut.wrapT=ut.wrapR=t.RepeatWrapping,ut.unpackAlignment=1,ut.needsUpdate=!0;var ht=["out vec3 vLocal;","void main(){ vLocal = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }"].join("\n"),dt=["precision highp float;","precision highp sampler3D;","uniform sampler3D uVol;","uniform vec3 uCam, uStar, uBg, uWarm, uCool, uRim;","uniform float uDens, uShadow, uEmit, uG, uSteps, uTwist;","in vec3 vLocal;","out vec4 outColor;","float h21(vec2 p){ return fract(sin(dot(p, vec2(12.9898,78.233)))*43758.5453); }","float h31(vec3 p){ return fract(sin(dot(p, vec3(12.9898,78.233,37.719)))*43758.5453); }","float dens(vec3 p){"," float r = length(p * vec3(1.0,1.34,1.0));"," if (r > 1.04) return 0.0;"," float a = uTwist * (1.0 - r*0.55);"," vec3 q = vec3(p.x - a*p.z, p.y, p.z + a*p.x);"," return texture(uVol, q*0.5 + 0.5).r * smoothstep(1.04, 0.96, r);","}","float hg(float c, float g){ float d = 1.0 + g*g - 2.0*g*c; return (1.0-g*g)/(12.566371*pow(max(d,1e-4),1.5)); }","vec3 sky(vec3 d){"," vec3 c = uBg * (0.60 + 0.85*(d.y*0.5+0.5)) + uCool*0.0085 + uRim*0.0035;"," for (int L=0; L<2; L++){"," float sc = L==0 ? 27.0 : 47.0;"," vec3 g = d*sc; vec3 cel = floor(g); vec3 f = g - cel;"," float br = h31(cel + float(L)*3.7);"," if (br > 0.60){"," vec3 off = clamp(vec3(h31(cel+11.7), h31(cel+23.3), h31(cel+31.9)), 0.10, 0.90);"," float dd = length(f - off);"," float s = exp(-dd*dd*220.0) * (br-0.60)*2.6 / sc * 32.0;"," c += s * mix(vec3(0.62,0.74,1.0), vec3(1.0,0.86,0.66), h31(cel+7.3));"," }"," }"," return c;","}","void main(){"," vec3 ro = uCam;"," vec3 rd = normalize(vLocal - uCam);"," vec3 iv = 1.0/rd;"," vec3 t0 = (vec3(-1.0)-ro)*iv, t1 = (vec3(1.0)-ro)*iv;"," vec3 tn = min(t0,t1), tf = max(t0,t1);"," float tE = max(max(tn.x,tn.y),tn.z), tX = min(min(tf.x,tf.y),tf.z);"," vec3 col = sky(rd);"," tE = max(tE, 0.0);"," if (tX > tE){"," int NS = int(uSteps);"," float dt = (tX-tE)/float(NS);"," float t = tE + dt*h21(gl_FragCoord.xy);"," float tr = 1.0; vec3 acc = vec3(0.0);"," for (int i=0; i<96; i++){"," if (i>=NS || tr<0.012) break;"," vec3 p = ro + rd*t;"," float d = dens(p);"," if (d > 0.004){"," vec3 tos = uStar - p; float sdist = length(tos);"," vec3 ld = tos/max(sdist,1e-3);"," float ls = min(sdist, 0.60)/6.0; float tau = 0.0;"," for (int j=0; j<6; j++) tau += dens(p + ld*(ls*(float(j)+0.5)));"," float sh = exp(-tau*ls*uShadow*uDens);"," float ph = hg(dot(rd, ld), uG);"," float fall = min(1.0/(0.10 + sdist*sdist*2.6), 5.0);"," vec3 tint = mix(uWarm, mix(uRim, uCool, 0.62)*0.52, clamp((sdist-0.06)*0.92, 0.0, 1.0));"," tint *= mix(vec3(1.0), vec3(1.30,0.84,0.58), (1.0-sh)*exp(-sdist*1.6));"," vec3 amb = (uCool*0.80 + uRim*0.20) * exp(-d*3.2) * 0.017;"," vec3 rad = tint*sh*fall*(0.30 + 9.0*ph) + amb;"," float a = 1.0 - exp(-d*uDens*dt);"," acc += tr * a * rad * uEmit;"," tr *= 1.0 - a;"," }"," vec3 sv = p - uStar; float r2 = dot(sv,sv);"," acc += tr * uWarm * (44.0/(1.0 + r2*r2*300000.0)) * dt;"," t += dt;"," }"," col = col*tr + acc;"," }"," col = (col*(2.10*col + 0.06)) / (col*(2.10*col + 0.90) + 0.16);"," outColor = vec4(pow(max(col, 0.0), vec3(1.0/2.2)), 1.0);","}"].join("\n"),vt={uVol:{value:ut},uCam:{value:new t.Vector3},uStar:{value:new t.Vector3(.1,-.05,.06)},uBg:{value:n(s)},uWarm:{value:n(i(r[0]))},uCool:{value:n(i(r[1%r.length]))},uRim:{value:n(i(r[2%r.length]))},uDens:{value:o.density},uShadow:{value:o.shadow},uEmit:{value:o.emission},uG:{value:o.forward},uSteps:{value:Math.max(16,Math.min(96,0|o.steps))},uTwist:{value:0}},pt=new t.ShaderMaterial({glslVersion:t.GLSL3,uniforms:vt,vertexShader:ht,fragmentShader:dt,side:t.BackSide,depthWrite:!1,depthTest:!1}),gt=new t.Scene,mt=new t.Mesh(new t.BoxGeometry(18,18,18),pt);mt.frustumCulled=!1,gt.add(mt);var xt=new t.PerspectiveCamera(46,e.width/Math.max(1,e.height),.05,200),bt=new t.Vector3,yt=new t.Vector3,Mt=o.distance,wt=0,St=0;function Ct(t){var a=e.mouse.active?e.mouse.x/Math.max(1,e.width)*2-1:0,r=e.mouse.active?e.mouse.y/Math.max(1,e.height)*2-1:0;wt+=.06*(.55*a-wt),St+=.06*(.38*-r-St),xt.position.set(Math.sin(wt)*Math.cos(St)*Mt,Math.sin(St)*Mt,Math.cos(wt)*Math.cos(St)*Mt),xt.lookAt(yt),mt.rotation.set(.22+t*o.spin*.31,.6+t*o.spin,t*o.spin*.14),mt.updateMatrixWorld(!0),bt.copy(xt.position),mt.worldToLocal(bt),vt.uCam.value.copy(bt),vt.uTwist.value=o.churn*Math.sin(.23*t)+.5*o.churn*Math.sin(.41*t+1)}return Ct(0),{resize:function(t,o){a.setPixelRatio(e.dpr),a.setSize(t,o,!1),xt.aspect=t/Math.max(1,o),xt.updateProjectionMatrix()},update:function(t){null!=t.density&&(vt.uDens.value=t.density),null!=t.emission&&(vt.uEmit.value=t.emission),null!=t.forward&&(vt.uG.value=t.forward),null!=t.shadow&&(vt.uShadow.value=t.shadow),null!=t.distance&&(Mt=t.distance),null!=t.spin&&(o.spin=t.spin)},draw:function(t){Ct(e.reduced?0:t),a.render(gt,xt)},destroy:function(){w(gt,[ut,pt,a])}}}}),y("spiralForge",{defaults:{stars:34e4,arms:2,pitch:.5,spin:1,bulge:.26,thickness:1,tilt:36,size:1,tint:.34,colors:["#ffc98a","#fff4e6","#a8c8ff"],background:"#03040c"},scene:function(t,e){var a=M(t,e),o=e.opts,r=u(o,["#ffc98a","#fff4e6","#a8c8ff"]);function i(e){var a=r[e%r.length];return new t.Vector3(a.r/255,a.g/255,a.b/255)}var n=new t.Scene,l=new t.PerspectiveCamera(40,Math.max(.2,e.width/e.height),.05,60);a.setClearColor(new t.Color(o.background||"#03040c"),1);var s=Math.max(6e4,Math.min(5e5,Math.round(o.stars||34e4))),c=Math.max(1,Math.round(o.arms||2)),f=Math.tan(Math.max(.08,Math.min(.9,o.pitch||.5))),h=Math.max(.15,o.thickness||1),d=Math.max(0,Math.min(.6,void 0===o.bulge?.26:o.bulge)),v=Math.max(.2,o.size||1),p=85893873;function g(){return((p=1664525*p+1013904223>>>0)>>>8)/16777216}function m(){return-Math.log(1e-7+g())}function x(t){return t<0?0:t>1?1:t}var b,y,S,C,k,T,P,R=new Float32Array(3*s),z=new Float32Array(3*s);for(b=0;b1.3&&(y=1.3*g()),g()<.1&&(y*=.35),S=.42*y*(2*g()-1),k=.12+.32*Math.pow(g(),1.3)+.22*x(1-y/.35),T=.72,C=g()*Math.PI*2;else{for(y=.95*(m()+m());y>3;)y=.95*(m()+m());S=(.028+.014*y)*h*(g()+g()+g()-1.5)*1.7;var L=x((y-.3)/.45)*x((3.5-y)/1.1),A=Math.log(Math.max(y,.1)/.26)/f,E=2*Math.PI/c,F=.34+y/3*.46;if(g()<.44*L){var q=1.15*(g()+g()+g()-1.5)*F;C=A+E*Math.floor(g()*c)+q,k=(P=g())<.05?.82+.18*g():P<.21?.6+.2*g():.18+.4*g(),T=.88}else C=g()*Math.PI*2,k=.14+.44*Math.pow(g(),1.15),T=.68;var D=C-A,B=(D-=Math.round(D/E)*E)/F;B>.32&&B<.8&&(T*=1-.55*L);var O=x((3-y)/1);T*=.25+.75*O*O}var I=.18*Math.pow(10,2.05*k),G=Math.pow(I,3.5);R[3*b]=y,R[3*b+1]=S,R[3*b+2]=C,z[3*b]=.0112*v*(.85+1.95*Math.pow(G,.1)),z[3*b+1]=k,z[3*b+2]=T*(.02+.115*Math.pow(G,.17))}var _=new t.BufferGeometry;_.setAttribute("position",new t.BufferAttribute(R,3)),_.setAttribute("aStar",new t.BufferAttribute(z,3)),_.boundingSphere=new t.Sphere(new t.Vector3(0,0,0),6);var W=new t.ShaderMaterial({uniforms:{uTime:{value:0},uSpin:{value:.1*(void 0===o.spin?1:o.spin)},uCore:{value:.4},uPx:{value:500},uTint:{value:Math.max(0,Math.min(1,void 0===o.tint?.34:o.tint))},uCool:{value:i(0)},uWarm:{value:i(1)},uHot:{value:i(2)}},vertexShader:["attribute vec3 aStar;","uniform float uTime, uSpin, uCore, uPx, uTint;","uniform vec3 uCool, uWarm, uHot;","varying vec3 vCol; varying float vI;","vec3 bb(float t){"," vec3 a=vec3(1.00,0.36,0.09), b=vec3(1.00,0.64,0.33), c=vec3(1.00,0.93,0.86);"," vec3 d=vec3(0.80,0.86,1.00), e=vec3(0.60,0.71,1.00);"," if(t<0.30) return mix(a,b,t/0.30);"," if(t<0.58) return mix(b,c,(t-0.30)/0.28);"," if(t<0.82) return mix(c,d,(t-0.58)/0.24);"," return mix(d,e,(t-0.82)/0.18);","}","void main(){"," float r = position.x, hgt = position.y, ph = position.z;"," float ang = ph + uTime * (uSpin / (r + uCore));"," vec3 p = vec3(cos(ang)*r, hgt, sin(ang)*r);"," vec4 mv = modelViewMatrix * vec4(p, 1.0);"," gl_Position = projectionMatrix * mv;"," float w = max(gl_Position.w, 0.02);"," float px = aStar.x * uPx / w;"," gl_PointSize = clamp(px, 1.0, 16.0);"," float sub = min(px, 1.0);"," vec3 t = bb(aStar.y);"," vec3 q = aStar.y < 0.5 ? mix(uCool, uWarm, aStar.y*2.0)"," : mix(uWarm, uHot, (aStar.y-0.5)*2.0);"," vCol = mix(t, t*0.35 + q*0.8, uTint);"," vI = aStar.z * sub * sub;","}"].join("\n"),fragmentShader:["precision mediump float;","varying vec3 vCol; varying float vI;","void main(){"," vec2 q = gl_PointCoord - 0.5;"," float d2 = dot(q, q);"," if (d2 > 0.25) discard;"," float a = 1.0 - d2 * 4.0;"," a = a * a * (0.35 + 0.65 * a);"," gl_FragColor = vec4(vCol * vI, a * vI);","}"].join("\n"),blending:t.AdditiveBlending,depthTest:!1,depthWrite:!1,transparent:!0}),U=new t.Points(_,W);U.frustumCulled=!1,n.add(U);var V=.65,H=Math.max(6,Math.min(80,o.tilt||36))*Math.PI/180,N=H;function j(){l.position.set(3.9*Math.cos(H)*Math.cos(V),3.9*Math.sin(H),3.9*Math.cos(H)*Math.sin(V)),l.lookAt(0,-.16,0)}return j(),{resize:function(t,o){a.setPixelRatio(e.dpr),a.setSize(t,o,!1),l.aspect=Math.max(.2,t/o),l.updateProjectionMatrix(),W.uniforms.uPx.value=.5*o*e.dpr/Math.tan(20*Math.PI/180)},update:function(e){void 0!==e.spin&&(W.uniforms.uSpin.value=.1*e.spin),void 0!==e.tint&&(W.uniforms.uTint.value=e.tint),e.background&&a.setClearColor(new t.Color(e.background),1),void 0!==e.tilt&&(N=e.tilt*Math.PI/180)},draw:function(t,o){if(W.uniforms.uTime.value=t,V=.65+.035*t,e.mouse&&e.mouse.active){var r=Math.max(0,Math.min(1,e.mouse.y/Math.max(1,e.height)));N=1.18+(.06-1.18)*r}var i=Math.min(1,2.5*(o||.016));H+=(N-H)*i,j(),a.render(n,l)},destroy:function(){w(n,[W,_,a])}}}}),y("ringedWorld",{defaults:{colors:["#f7e7c8","#d59a5c","#7a4f34","#cfe0ff"],background:"#03040a",bands:9,ringInner:1.34,ringOuter:2.44,ringTau:.74,spin:.055,sunElev:15.5,haze:.34,sun:4,exposure:1.05,distance:4.55},scene:function(t,e){var a=M(t,e),o=e.opts,r=u(o,["#f7e7c8","#d59a5c","#7a4f34","#cfe0ff"]);function i(e){return new t.Vector3(Math.pow(e.r/255,2.2),Math.pow(e.g/255,2.2),Math.pow(e.b/255,2.2))}function n(t){return i(r[t%r.length])}for(var s=i(l(o.background||"#03040a")),c=1024,f=new Float32Array(65536),h=20240719,d=0;d>>0,f[d]=h/4294967296;function v(t,e,a,o){return f[((t&a)+o&255)<<8|e+7*o&255]}function p(t,e,a,o){var r=Math.floor(t),i=Math.floor(e),n=t-r,l=e-i;n=n*n*(3-2*n),l=l*l*(3-2*l);var s=v(r,i,a,o)*(1-n)+v(r+1,i,a,o)*n;return s+(v(r,i+1,a,o)*(1-n)+v(r+1,i+1,a,o)*n-s)*l}function g(t,e,a,o,r,i){for(var n=.5,l=0,s=0,c=a,f=0;f.5&&(D-=1),D<-.5&&(D+=1);var B=2.1*(T-S[q]),O=Math.sqrt(D*D+B*B)/C[q];if(O<1.7){var I=(1-O/1.7)*(1-O/1.7),G=Math.atan2(B,D)+1.9*O;F=Math.max(F,I),A+=I*(.3+.34*(p(9*Math.cos(G)+40,9*Math.sin(G)+5*O,255,7)-.5))}}A=A<0?0:A>1?1:A,m[x++]=255*A|0,m[x++]=255*g(z,T,64,.18,5,3)|0,m[x++]=255*F|0,m[x++]=255}var _=new t.DataTexture(m,c,512,t.RGBAFormat);_.wrapS=t.RepeatWrapping,_.wrapT=t.ClampToEdgeWrapping,_.minFilter=t.LinearMipmapLinearFilter,_.magFilter=t.LinearFilter,_.generateMipmaps=!0,_.needsUpdate=!0;var W={value:new t.Vector3(1,0,0)},U={value:new t.Vector3},V={value:o.ringInner},H={value:o.ringOuter},N={value:o.ringTau},j={value:0},K={value:o.sun},J={value:n(3)},X="out vec3 vP; void main(){ vP=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }",Y=["uniform float uRi, uRo, uTauK;","float h11(float x){ return fract(sin(x*127.1)*43758.5453); }","float vn1(float x){ float i=floor(x), f=fract(x); f=f*f*(3.0-2.0*f); return mix(h11(i),h11(i+1.0),f); }","float ringTau(float r){"," if (r < uRi || r > uRo) return 0.0; float x = (r - uRi)/(uRo - uRi);"," float fw = fwidth(x)*1.7;"," float d = 0.58 + 0.42*sin(x*7.5 - 0.8);"," d *= mix(1.0, 0.72 + 0.58*vn1(x*27.0 + 3.1), 1.0 - smoothstep(0.0, 0.075, fw));"," d *= mix(1.0, 0.66 + 0.68*vn1(x*104.0), 1.0 - smoothstep(0.0, 0.034, fw));"," d *= smoothstep(0.022, 0.070, abs(x - 0.47));"," float g = (x - 0.80)/0.013; d *= 1.0 - 0.85*exp(-g*g);"," return max(d*smoothstep(0.0,0.05,x)*smoothstep(1.0,0.93,x), 0.0) * uTauK;","}"].join("\n"),Q=new t.Scene,Z=new t.PerspectiveCamera(38,e.width/Math.max(1,e.height),.05,300),$={uMap:{value:_},uL:W,uCam:U,uSpin:j,uSun:K,uRi:V,uRo:H,uTauK:N,uIce:J,uHaze:{value:o.haze},uC0:{value:n(0)},uC1:{value:n(1)},uC2:{value:n(2)},uHot:{value:n(1).clone().multiply(new t.Vector3(1.75,1.05,.72))}},tt=new t.ShaderMaterial({glslVersion:t.GLSL3,uniforms:$,vertexShader:"out vec2 vUv; out vec3 vP;\nvoid main(){ vUv=uv; vP=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }",fragmentShader:["precision highp float;","uniform sampler2D uMap;","uniform vec3 uL,uCam,uC0,uC1,uC2,uHot,uIce; uniform float uSpin,uSun,uHaze;","in vec2 vUv; in vec3 vP; out vec4 outColor;",Y,"float ringShadow(vec3 p){"," if (abs(uL.y) < 1e-4) return 1.0; float s = -p.y/uL.y;"," if (s <= 0.0) return 1.0;"," return exp(-ringTau(length((p + uL*s).xz))/max(abs(uL.y), 0.05));","}","void main(){"," vec3 n = normalize(vP); vec4 tx = texture(uMap, vec2(vUv.x + uSpin, vUv.y));"," vec3 alb = mix(mix(uC2, uC1, smoothstep(0.0,0.54,tx.r)), uC0, smoothstep(0.46,1.0,tx.r));"," alb = mix(alb, uHot, tx.b*0.80) * (0.84 + 0.30*tx.g); float nl = dot(n, uL);"," float wrap = clamp((nl + 0.14)/1.14, 0.0, 1.0), li = wrap*wrap;"," float mu = clamp(dot(n, normalize(uCam - vP)), 0.0, 1.0);"," float ld = exp(-uHaze*(1.0/max(mu,0.09) - 1.0));"," vec3 c = alb*(li*ld*ringShadow(vP)*uSun + 0.010);"," outColor = vec4(c + alb*uIce*0.055*smoothstep(-0.55,0.25,-nl), 1.0);","}"].join("\n")});Q.add(new t.Mesh(new t.SphereGeometry(1,128,80),tt));var et={uL:W,uCam:U,uBeta:{value:new t.Vector3(2.6,6.1,15.5)},uSky:{value:n(3)},uRa:{value:1.105},uHs:{value:.03},uSunI:{value:5.6}},at=new t.ShaderMaterial({glslVersion:t.GLSL3,uniforms:et,transparent:!0,blending:t.AdditiveBlending,side:t.BackSide,depthTest:!1,depthWrite:!1,vertexShader:X,fragmentShader:["precision highp float;","uniform vec3 uL,uCam,uBeta,uSky; uniform float uRa,uHs,uSunI;","in vec3 vP; out vec4 outColor;","vec2 sph(vec3 ro, vec3 rd, float r){ float b=dot(ro,rd), c=dot(ro,ro)-r*r, d=b*b-c;"," if (d < 0.0) return vec2(1.0,-1.0); d=sqrt(d); return vec2(-b-d,-b+d); }","float dns(vec3 p){ return exp(-(length(p)-1.0)/uHs); }","float odSun(vec3 p){"," float sc = -dot(p, uL), od = 0.0, f = sph(p, uL, uRa).y/5.0;"," float occ = sc > 0.0 ? smoothstep(0.985, 1.030, length(p + uL*sc)) : 1.0;"," for (int i=0;i<5;i++) od += dns(p + uL*(f*(float(i)+0.5)));"," return od*f + (1.0 - occ)*40.0;","}","void main(){"," vec3 ro = uCam, rd = normalize(vP - uCam); vec2 a = sph(ro, rd, uRa);"," if (a.y <= a.x) discard;"," float t0 = max(a.x, 0.0), t1 = a.y; vec2 s = sph(ro, rd, 1.0);"," if (s.y > 0.0 && s.x > t0) t1 = min(t1, s.x);"," float mu = dot(rd, uL), ds = (t1-t0)/16.0, odV = 0.0;"," vec3 acc = vec3(0.0), beta = uBeta*normalize(uSky+0.35)*1.7;"," for (int i=0;i<16;i++){"," vec3 p = ro + rd*(t0 + ds*(float(i)+0.5)); float d = dns(p)*ds; odV += d;"," acc += d*exp(-beta*(odSun(p) + odV));"," }"," outColor = vec4(acc*beta*(0.0596831*(1.0+mu*mu))*uSunI, 1.0);","}"].join("\n")}),ot=new t.Mesh(new t.SphereGeometry(1.105,72,48),at);ot.renderOrder=2,Q.add(ot);for(var rt=[],it=[],nt=0;nt<2;nt++)for(var lt=0;lt<=320;lt++){var st=lt/320*Math.PI*2,ct=nt?o.ringOuter:o.ringInner;rt.push(Math.cos(st)*ct,0,Math.sin(st)*ct)}for(var ft=0;ft<320;ft++)it.push(ft,321+ft,ft+1,ft+1,321+ft,322+ft);var ut=new t.BufferGeometry;ut.setAttribute("position",new t.Float32BufferAttribute(rt,3)),ut.setIndex(it);var ht={uL:W,uCam:U,uRi:V,uRo:H,uTauK:N,uIce:J,uSun:K,uWarm:{value:n(0)}},dt=new t.ShaderMaterial({glslVersion:t.GLSL3,uniforms:ht,transparent:!0,side:t.DoubleSide,depthWrite:!1,vertexShader:X,fragmentShader:["precision highp float;","uniform vec3 uL,uCam,uIce,uWarm; uniform float uSun;","in vec3 vP; out vec4 outColor;",Y,"void main(){"," float tau = ringTau(length(vP.xz)); if (tau <= 0.0005) discard;"," vec3 rd = normalize(vP - uCam);"," float sc = -dot(vP, uL); vec3 icy = mix(uIce, uWarm, 0.30);"," float shp = sc > 0.0 ? smoothstep(0.985, 1.075, length(vP + uL*sc)) : 1.0;"," float el = max(abs(uL.y), 0.02);"," float alpha = 1.0 - exp(-tau/max(abs(rd.y), 0.42));"," float trans = exp(-tau/el);"," float unlit = step(uL.y*(-rd.y), 0.0);"," vec3 c = icy*shp*(0.10 + 0.85*el)*uSun*0.30*(1.0 - 0.78*unlit);"," c += icy*shp*trans*unlit*uSun*0.30 + icy*0.010;"," outColor = vec4(c, alpha);","}"].join("\n")}),vt=new t.Mesh(ut,dt);vt.renderOrder=3,Q.add(vt);var pt={uBg:{value:s},uCool:{value:n(3)}},gt=new t.ShaderMaterial({glslVersion:t.GLSL3,uniforms:pt,side:t.BackSide,depthWrite:!1,depthTest:!1,vertexShader:X,fragmentShader:["precision highp float;","uniform vec3 uBg,uCool;","in vec3 vP; out vec4 outColor;","float h31(vec3 p){ return fract(sin(dot(p, vec3(12.9898,78.233,37.719)))*43758.5453); }","void main(){"," vec3 d = normalize(vP), c = uBg*(0.75+0.5*(d.y*0.5+0.5)) + uCool*0.004;"," for (int L=0;L<3;L++){"," float sc = L==0 ? 34.0 : (L==1 ? 58.0 : 92.0);"," vec3 g = d*sc, cel = floor(g), f = g - cel; float br = h31(cel + float(L)*4.1);"," if (br > 0.70){"," vec3 of = clamp(vec3(h31(cel+11.7), h31(cel+23.3), h31(cel+31.9)), 0.12, 0.88);"," float dd = length(f - of);"," c += exp(-dd*dd*300.0)*(br-0.70)*3.4*mix(vec3(0.65,0.76,1.0), vec3(1.0,0.87,0.7), h31(cel+7.3));"," } }"," outColor = vec4(c, 1.0);","}"].join("\n")}),mt=new t.Mesh(new t.SphereGeometry(150,32,24),gt);mt.renderOrder=-1,Q.add(mt);var xt=new t.WebGLRenderTarget(Math.max(2,e.width*e.dpr),Math.max(2,e.height*e.dpr),{type:t.HalfFloatType,minFilter:t.LinearFilter,magFilter:t.LinearFilter,depthBuffer:!0}),bt={uTex:{value:xt.texture},uExp:{value:o.exposure}},yt=new t.ShaderMaterial({glslVersion:t.GLSL3,uniforms:bt,depthTest:!1,depthWrite:!1,vertexShader:"out vec2 vU; void main(){ vU=uv; gl_Position=vec4(position.xy,0.0,1.0); }",fragmentShader:["precision highp float;","uniform sampler2D uTex; uniform float uExp;","in vec2 vU; out vec4 outColor;","void main(){"," vec3 c = texture(uTex, vU).rgb * uExp * (1.0 - 0.30*dot(vU-0.5, vU-0.5));"," c = (c*(2.30*c+0.05))/(c*(2.30*c+0.95)+0.16);"," outColor = vec4(pow(max(c, 0.0), vec3(1.0/2.2)), 1.0);","}"].join("\n")}),Mt=new t.Scene;Mt.add(new t.Mesh(new t.PlaneGeometry(2,2),yt));var wt=new t.OrthographicCamera(-1,1,1,-1,0,1),St=-.245,Ct=new t.Vector3;function kt(t){var a=e.mouse.active?e.mouse.y/Math.max(1,e.height):.52;St+=.05*(-.055-.4*(1-a)-St);var r=.9+.048*t,i=o.distance;Z.position.set(Math.cos(r)*Math.cos(St)*i,Math.sin(St)*i,Math.sin(r)*Math.cos(St)*i),Z.lookAt(Ct),U.value.copy(Z.position);var n=o.sunElev*Math.PI/180+.085*Math.sin(.085*t),l=r+.86+.22*Math.sin(.062*t);W.value.set(Math.cos(l)*Math.cos(n),Math.sin(n),Math.sin(l)*Math.cos(n)).normalize(),j.value=t*o.spin}return kt(0),{resize:function(t,o){a.setPixelRatio(e.dpr),a.setSize(t,o,!1),xt.setSize(Math.max(2,t*e.dpr),Math.max(2,o*e.dpr)),Z.aspect=t/Math.max(1,o),Z.updateProjectionMatrix()},update:function(t){null!=t.exposure&&(bt.uExp.value=t.exposure),null!=t.ringTau&&(N.value=t.ringTau),null!=t.sun&&(K.value=t.sun),null!=t.haze&&($.uHaze.value=t.haze),null!=t.sunElev&&(o.sunElev=t.sunElev),null!=t.distance&&(o.distance=t.distance),null!=t.spin&&(o.spin=t.spin)},draw:function(t){kt(e.reduced?0:t),a.setRenderTarget(xt),a.render(Q,Z),a.setRenderTarget(null),a.render(Mt,wt)},destroy:function(){w(Mt,[]),w(Q,[_,tt,at,dt,gt,yt,xt,a])}}}}),y("gravitySim",{defaults:{grid:160,speed:1,mass:9e3,separation:40,eccentricity:.45,softening:2.2,pointSize:1,glow:1,exposure:1.6,bloom:.45,interactive:!0,colors:["#2f4bff","#22d3ee","#ffd08a","#fff3df"],background:"#03040a"},scene:function(t,e){var a=M(t,e),o=a.getContext(),r=!(!o.getExtension("EXT_color_buffer_float")&&!o.getExtension("WEBGL_color_buffer_float")),i=e.opts,n=u(i,["#2f4bff","#22d3ee","#ffd08a","#fff3df"]),s=r?Math.max(32,Math.min(160,Math.round(i.grid||160))):44,c=s*s;function f(e){var a=n[Math.round(e/3*(n.length-1))];return new t.Vector3(Math.pow(a.r/255,2.2),Math.pow(a.g/255,2.2),Math.pow(a.b/255,2.2))}var h=l(i.background||"#03040a"),d=new t.Vector3(Math.pow(h.r/255,2.2),Math.pow(h.g/255,2.2),Math.pow(h.b/255,2.2)),v=i.mass||9e3,p=i.separation||40,g=i.softening||2.2,m=Math.max(0,Math.min(.7,void 0===i.eccentricity?.45:i.eccentricity)),x=Math.sqrt(2*v/(p*p*p)),b=1.1*v,y=Math.sqrt(v/(1.2*g))+10,S=.3*p,C=r?0:1,k=new t.Vector3,T=new t.Vector3,P=new t.Vector3;function R(t,e){var a,o=Math.PI+t*x,r=o;for(a=0;a<5;a++)r-=(r-m*Math.sin(r)-o)/(1-m*Math.cos(r));var i=p*(1-m*Math.cos(r))*.5,n=Math.atan2(Math.sqrt(1-m*m)*Math.sin(r),Math.cos(r)-m);return e.set(Math.cos(n)*i,0,Math.sin(n)*i)}R(0,k),R(.02,T),R(-.02,P),T.sub(P).multiplyScalar(25);var z,L,A,E,F=new Float32Array(4*c),q=new Float32Array(4*c);for(z=0;z0.5)c=pow(max(c,0.0),vec3(0.4545));gl_FragColor=vec4(c,1.0);}",{uBg:{value:d},uAsp:{value:1},uEnc:{value:C},uHalo:{value:f(1).lerp(f(0),.55)}}),dt=new t.Scene,vt=new t.BufferGeometry,pt=new t.PerspectiveCamera(45,1.7,1,900),gt=new Float32Array(2*c),mt=new Float32Array(c),xt=new Float32Array(3*c);for(z=0;z0.5){p=texture2D(tPos,aRef).xyz;sp=length(texture2D(tVel,aRef).xyz);}vS=clamp(sp/uVMax,0.0,1.0);vec4 mv=modelViewMatrix*vec4(p,1.0);gl_Position=projectionMatrix*mv;gl_PointSize=clamp(uSize*uDpr*(190.0/max(1.0,-mv.z))*(1.2-0.35*vS),1.0,4.5);}",fragmentShader:"uniform vec3 uC0,uC1,uC2,uC3;uniform float uGlow,uEnc;varying float vS;void main(){vec2 q=gl_PointCoord-0.5;float d=dot(q,q);if(d>0.25)discard;float m=exp(-d*13.0)*(1.0-d*4.0);vec3 c=mix(uC0,uC1,smoothstep(0.0,0.34,vS));c=mix(c,uC2,smoothstep(0.3,0.68,vS));c=mix(c,uC3,smoothstep(0.62,1.0,vS));c*=(0.02+2.4*vS*vS*vS)*uGlow*m;if(uEnc>0.5)c=pow(max(c,0.0),vec3(0.4545));gl_FragColor=vec4(c,1.0);}",transparent:!0,blending:t.AdditiveBlending,depthTest:!1,depthWrite:!1,uniforms:{tPos:{value:null},tVel:{value:null},uUseTex:{value:r?1:0},uSize:{value:i.pointSize||1},uVMax:{value:y},uDpr:{value:e.dpr},uGlow:{value:void 0===i.glow?1:i.glow},uEnc:{value:C},uC0:{value:f(0)},uC1:{value:f(1)},uC2:{value:f(2)},uC3:{value:f(3)}}}),yt=new t.Points(vt,bt);yt.frustumCulled=!1,dt.add(yt);var Mt=new t.SphereGeometry(.8,14,10),wt=new t.ShaderMaterial({vertexShader:"varying vec3 vN;void main(){vN=normalize(normalMatrix*normal);gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}",fragmentShader:"uniform vec3 uC;uniform float uEnc;varying vec3 vN;void main(){vec3 c=uC*(0.3+2.2*pow(max(0.0,vN.z),1.5));if(uEnc>0.5)c=pow(c,vec3(0.4545));gl_FragColor=vec4(c,1.0);}",blending:t.AdditiveBlending,transparent:!0,depthWrite:!1,uniforms:{uC:{value:f(3)},uEnc:{value:C}}}),St=[new t.Mesh(Mt,wt),new t.Mesh(Mt,wt)];dt.add(St[0],St[1]);var Ct=0,kt=0,Tt=new t.Vector3,Pt=new t.Vector3;function Rt(){R(Ct,k),ut[0].set(k.x,0,k.z,v),ut[1].set(-k.x,0,-k.z,v),ut[2].set(Tt.x,Tt.y,Tt.z,kt*v*.85),St[0].position.set(k.x,0,k.z),St[1].position.set(-k.x,0,-k.z)}function zt(t){Ct+=t,Rt(),r?(at.uniforms.tPos.value=st,at.uniforms.tVel.value=ct,at.uniforms.uDt.value=t,K.material=at,a.setRenderTarget($[ft]),a.render(H,N),ot.uniforms.tPos.value=st,ot.uniforms.tVel.value=$[ft].texture,ot.uniforms.uDt.value=t,K.material=ot,a.setRenderTarget(Z[ft]),a.render(H,N),ct=$[ft].texture,st=Z[ft].texture,ft=1-ft,a.setRenderTarget(null)):function(t){for(var e=0;e>2),l=Math.max(2,i>>2);rt.setSize(r,i),it.setSize(n,l),lt.uniforms.uTexel.value.set(1/n,1/l)}},update:function(t){void 0!==t.glow&&(bt.uniforms.uGlow.value=t.glow),void 0!==t.pointSize&&(bt.uniforms.uSize.value=t.pointSize),nt&&void 0!==t.exposure&&(nt.uniforms.uExp.value=t.exposure),nt&&void 0!==t.bloom&&(nt.uniforms.uBloom.value=t.bloom)},draw:function(t,o){var n=Math.min(o||1/60,1/30)*(void 0===i.speed?1:i.speed),l=!1!==i.interactive&&e.mouse&&e.mouse.active?1:0;if(l){Pt.set(e.mouse.x/Math.max(1,e.width)*2-1,1-e.mouse.y/Math.max(1,e.height)*2,.5),Pt.unproject(pt).sub(pt.position).normalize();var s=Pt.y<-.03?-pt.position.y/Pt.y:90;Tt.copy(pt.position).addScaledVector(Pt,Math.max(6,Math.min(240,s)))}kt+=.06*(l-kt),e.reduced?Rt():(zt(.5*n),zt(.5*n)),At(t),bt.uniforms.tPos.value=st,bt.uniforms.tVel.value=ct,a.setRenderTarget(r?rt:null),K.material=ht,a.autoClear=!0,a.render(H,N),a.autoClear=!1,a.render(dt,pt),a.autoClear=!0,r&&(K.material=lt,a.setRenderTarget(it),a.render(H,N),a.setRenderTarget(null),K.material=nt,a.render(H,N))},destroy:function(){var t=[ht,bt,wt,Mt,j,a];r&&(t=t.concat([Z[0],Z[1],$[0],$[1],rt,it,tt,et,at,ot,lt,nt])),w(dt,t)}}}}),y("starGlare",{defaults:{speed:1,bloomStrength:1,bloomRadius:1.15,threshold:1,streak:.7,aberration:1,exposure:.9,colors:["#fff4d6","#ffcf8a","#a8c8ff","#7d8dff"],background:"#03040a"},scene:function(t,e){var a=M(t,e);a.toneMapping=t.NoToneMapping,a.setClearColor(0,1);var o=u(e.opts,["#fff4d6","#ffcf8a","#a8c8ff","#7d8dff"]);function r(e){return new t.Vector3(Math.pow(e.r/255,2.2),Math.pow(e.g/255,2.2),Math.pow(e.b/255,2.2))}var s=r(o[0]),c=r(o[1%o.length]),f=r(o[2%o.length]),h=r(o[3%o.length]),d=r(l(e.opts.background||"#03040a")),v=new t.Scene,p=new t.PerspectiveCamera(42,e.width/Math.max(1,e.height),.1,400);p.position.set(0,0,15);var g=new t.ShaderMaterial({uniforms:{uC:{value:s},uI:{value:52}},vertexShader:"varying vec2 vUv; void main(){ vUv=uv; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }",fragmentShader:"varying vec2 vUv; uniform vec3 uC; uniform float uI;void main(){ vec2 p=vUv*2.0-1.0; float r=length(p); if(r>1.0) discard; float k=max(0.0,1.0-r); float core=k*k*k*k*k*k*k*k; float halo=exp(-r*3.4)-exp(-3.4); gl_FragColor=vec4(uC*uI*(core+halo*0.055),1.0); }",transparent:!0,blending:t.AdditiveBlending,depthWrite:!1,side:t.DoubleSide}),m=new t.Group;v.add(m);var x=new t.Mesh(new t.PlaneGeometry(3.6,3.6),g),b=new t.Mesh(new t.SphereGeometry(.16,20,14),g),y=new t.Mesh(new t.PlaneGeometry(1.7,1.7),g.clone());y.material.uniforms.uC.value=c,y.material.uniforms.uI.value=15;var S=new t.Mesh(new t.SphereGeometry(.07,14,10),y.material);m.add(x),m.add(b),m.add(y),m.add(S);var C=new t.Color(o[0].r/255,o[0].g/255,o[0].b/255),k=new t.PointLight(C,420,0,2);v.add(k),v.add(new t.AmbientLight(1319226,.35));var T=new t.MeshStandardMaterial({color:790552,roughness:.92,metalness:.05}),P=new t.Mesh(new t.SphereGeometry(2.6,96,64),T);P.position.z=3.4,v.add(P);var R=new t.ShaderMaterial({uniforms:{uC:{value:f},uSun:{value:new t.Vector3(0,0,-1)},uI:{value:2.6},uEcl:{value:0},uCen:{value:new t.Vector3},uRb:{value:2.6},uRa:{value:3.5}},vertexShader:"varying vec3 vR; varying vec3 vV;uniform vec3 uCen;void main(){ vec4 wp=modelMatrix*vec4(position,1.0); vR=wp.xyz-uCen; vV=normalize(cameraPosition-wp.xyz); gl_Position=projectionMatrix*viewMatrix*wp; }",fragmentShader:"varying vec3 vR; varying vec3 vV; uniform vec3 uC; uniform vec3 uSun;uniform float uI; uniform float uEcl; uniform float uRb; uniform float uRa;void main(){ vec3 V=-vV; vec3 pr=vR-V*dot(vR,V); float b=length(pr); float alt=clamp((b-uRb)/max(0.001,uRa-uRb),0.0,1.0); float dens=exp(-alt*4.5)*(1.0-alt); vec3 sd=uSun-V*dot(uSun,V); float ls=length(sd); float sn=(ls>1e-4&&b>1e-4)?max(0.0,dot(pr/b,sd/ls)):0.0; gl_FragColor=vec4(uC*uI*dens*(0.03+4.0*sn*sn*sn+0.9*uEcl),1.0); }",transparent:!0,blending:t.AdditiveBlending,depthWrite:!1,side:t.BackSide}),z=new t.Mesh(new t.SphereGeometry(3.5,48,32),R);function L(e,a,o,r,i){for(var l=new Float32Array(3*e),s=new Float32Array(e),c=new Float32Array(e),u=new Float32Array(3*e),h=0;h1.0) discard; gl_FragColor=vec4(vC*exp(-r2*uSoft),1.0); }",transparent:!0,blending:t.AdditiveBlending,depthWrite:!1}),E=new t.Points(L(950,90,.5,s,1.3),A);v.add(E);var F=A.clone();F.uniforms.uSoft.value=3.4;var q=new t.Points(L(80,24,2.6,h,.03),F);function D(e){return new t.WebGLRenderTarget(2,2,{type:t.HalfFloatType,minFilter:t.LinearFilter,magFilter:t.LinearFilter,wrapS:t.ClampToEdgeWrapping,wrapT:t.ClampToEdgeWrapping,depthBuffer:!!e})}v.add(q);var B,O=D(!0),I=D(),G=D(),_=D(),W=D(),U=[],V=[];for(B=0;B<4;B++)U.push(D()),V.push(D());var H=[O,I,G,_,W].concat(U,V);function N(e,a){return new t.ShaderMaterial({uniforms:a,vertexShader:"varying vec2 vUv; void main(){ vUv=uv; gl_Position=vec4(position.xy,0.0,1.0); }",fragmentShader:e,depthTest:!1,depthWrite:!1})}var j="vec3 tap(sampler2D s, vec2 uv, vec2 o){ return texture2D(s, uv+o).rgb; }\n",K=N("varying vec2 vUv; uniform sampler2D tS; uniform float uT;void main(){ vec3 c=texture2D(tS,vUv).rgb; float l=max(max(c.r,c.g),c.b); float kn=0.55; float s=clamp(l-uT+kn,0.0,2.0*kn); s=s*s/(4.0*kn); float w=max(s,l-uT)/max(l,1e-4); gl_FragColor=vec4(c*w,1.0); }",{tS:{value:null},uT:{value:1}}),J=N("varying vec2 vUv; uniform sampler2D tS; uniform vec2 uTx;\n"+j+"void main(){ vec2 t=uTx; vec3 o=tap(tS,vUv,vec2(0.0))*0.125 +(tap(tS,vUv,vec2(-2,2)*t)+tap(tS,vUv,vec2(2,2)*t)+tap(tS,vUv,vec2(-2,-2)*t)+tap(tS,vUv,vec2(2,-2)*t))*0.03125 +(tap(tS,vUv,vec2(0,2)*t)+tap(tS,vUv,vec2(-2,0)*t)+tap(tS,vUv,vec2(2,0)*t)+tap(tS,vUv,vec2(0,-2)*t))*0.0625 +(tap(tS,vUv,vec2(-1,1)*t)+tap(tS,vUv,vec2(1,1)*t)+tap(tS,vUv,vec2(-1,-1)*t)+tap(tS,vUv,vec2(1,-1)*t))*0.125; gl_FragColor=vec4(o,1.0); }",{tS:{value:null},uTx:{value:new t.Vector2}}),X=N("varying vec2 vUv; uniform sampler2D tS; uniform vec2 uDir;\n"+j+"void main(){ vec3 s=tap(tS,vUv,vec2(0.0))*0.227027; s+=(tap(tS,vUv,uDir*1.384615)+tap(tS,vUv,-uDir*1.384615))*0.316216; s+=(tap(tS,vUv,uDir*3.230769)+tap(tS,vUv,-uDir*3.230769))*0.070270; gl_FragColor=vec4(s,1.0); }",{tS:{value:null},uDir:{value:new t.Vector2}}),Y=N("varying vec2 vUv; uniform sampler2D tS; uniform sampler2D tAdd; uniform vec2 uTx; uniform float uR;\n"+j+"void main(){ vec2 t=uTx*uR; vec3 s=tap(tS,vUv,vec2(-1,1)*t)+tap(tS,vUv,vec2(0,1)*t)*2.0+tap(tS,vUv,vec2(1,1)*t) +tap(tS,vUv,vec2(-1,0)*t)*2.0+tap(tS,vUv,vec2(0.0))*4.0+tap(tS,vUv,vec2(1,0)*t)*2.0 +tap(tS,vUv,vec2(-1,-1)*t)+tap(tS,vUv,vec2(0,-1)*t)*2.0+tap(tS,vUv,vec2(1,-1)*t); gl_FragColor=vec4(s*0.0625+texture2D(tAdd,vUv).rgb,1.0); }",{tS:{value:null},tAdd:{value:null},uTx:{value:new t.Vector2},uR:{value:1.15}}),Q=N("varying vec2 vUv; uniform sampler2D tS; uniform float uStep; uniform vec3 uTint; uniform float uAtt;void main(){ vec3 s=texture2D(tS,vUv).rgb; float wsum=1.0; for(int i=1;i<=14;i++){ float fi=float(i); float w=pow(uAtt,fi); s+=(texture2D(tS,vUv+vec2(uStep*fi,0.0)).rgb+texture2D(tS,vUv-vec2(uStep*fi,0.0)).rgb)*w; wsum+=2.0*w; } gl_FragColor=vec4(s/wsum*uTint,1.0); }",{tS:{value:null},uStep:{value:.002},uAtt:{value:.8},uTint:{value:new t.Vector3(1,1,1)}}),Z=N("varying vec2 vUv; uniform sampler2D tS; uniform sampler2D tB; uniform sampler2D tK; uniform sampler2D tG;uniform float uBloom; uniform float uStreak; uniform float uAb; uniform float uExp; uniform vec3 uBg;vec3 ghost(vec2 d){ return texture2D(tG,0.5-d*0.45).rgb*vec3(0.09,0.05,0.03) + texture2D(tG,0.5-d*0.82).rgb*vec3(0.03,0.06,0.10) + texture2D(tG,0.5+d*1.55).rgb*vec3(0.05,0.03,0.08); }vec3 grab(vec2 uv){ vec2 d=uv-0.5; return texture2D(tS,uv).rgb + texture2D(tB,uv).rgb*uBloom + texture2D(tK,uv).rgb*uStreak + ghost(d)*uBloom; }vec3 aces(vec3 x){ return clamp((x*(2.51*x+0.03))/(x*(2.43*x+0.59)+0.14),0.0,1.0); }void main(){ vec2 d=vUv-0.5; float r2=dot(d,d); float ca=uAb*0.007*r2; vec3 c; c.r=grab(0.5+d*(1.0-ca)).r; c.g=grab(vUv).g; c.b=grab(0.5+d*(1.0+ca)).b; c+=uBg*(1.2+16.0*exp(-2.4*r2)); c=aces(c*uExp)*(1.0-0.55*r2); gl_FragColor=vec4(pow(max(c,vec3(0.0)),vec3(1.0/2.2)),1.0); }",{tS:{value:null},tB:{value:null},tK:{value:null},tG:{value:null},uBg:{value:d},uBloom:{value:1},uStreak:{value:.7},uAb:{value:1},uExp:{value:.9}}),$=new t.PlaneGeometry(2,2),tt=new t.Mesh($,K),et=new t.Scene;et.add(tt);var at=new t.OrthographicCamera(-1,1,1,-1,0,1);function ot(t,e){tt.material=t,a.setRenderTarget(e||null),a.render(et,at)}function rt(t,e){X.uniforms.tS.value=t.texture,X.uniforms.uDir.value.set(1/t.width,0),ot(X,e),X.uniforms.tS.value=e.texture,X.uniforms.uDir.value.set(0,1/t.height),ot(X,t)}function it(t,a){var o=Math.max(2,Math.floor(t*e.dpr)),r=Math.max(2,Math.floor(a*e.dpr));O.setSize(o,r);var i=Math.max(2,o>>1),n=Math.max(2,r>>1);I.setSize(i,n),G.setSize(i,n),_.setSize(i,n),W.setSize(i,n);for(var l=0;l<4;l++)i=Math.max(2,i>>1),n=Math.max(2,n>>1),U[l].setSize(i,n),V[l].setSize(i,n);A.uniforms.uScale.value=.55*r,F.uniforms.uScale.value=.55*r}it(e.width,e.height);var nt=e.opts,lt=0,st=0;function ct(){K.uniforms.uT.value=nt.threshold,Y.uniforms.uR.value=nt.bloomRadius,Z.uniforms.uBloom.value=.3*nt.bloomStrength,Z.uniforms.uStreak.value=.6*nt.streak,Z.uniforms.uAb.value=nt.aberration,Z.uniforms.uExp.value=nt.exposure,Q.uniforms.uTint.value.set(.7,.83,1.22)}return ct(),{resize:function(t,o){a.setPixelRatio(e.dpr),a.setSize(t,o,!1),p.aspect=t/Math.max(1,o),p.updateProjectionMatrix(),it(t,o)},update:function(t){nt=t,ct()},draw:function(t){var o=t*(nt.speed||1)*.6,r=4.3*Math.sin(o),n=1.5*Math.cos(2*o)+.62;if(e.mouse&&e.mouse.active){var l=e.mouse.x/Math.max(1,e.width)*2-1,s=-(e.mouse.y/Math.max(1,e.height)*2-1);lt+=.12*(4.6*l-r-lt),st+=.12*(2.8*s-n-st)}else lt+=.05*(0-lt),st+=.05*(0-st);P.position.x=r+lt,P.position.y=n+st,P.rotation.y=.35*o,P.rotation.x=.2,z.position.copy(P.position),R.uniforms.uCen.value.copy(P.position),R.uniforms.uSun.value.copy(P.position).multiplyScalar(-1).normalize();var c=i(1-Math.sqrt(P.position.x*P.position.x+P.position.y*P.position.y)/2.6,0,1);R.uniforms.uEcl.value=c*c*1.4;var f=3*o;y.position.set(.62*Math.cos(f),.2*Math.sin(f),.62*Math.sin(f)),S.position.copy(y.position),x.quaternion.copy(p.quaternion),y.quaternion.copy(p.quaternion),A.uniforms.uTime.value=t,F.uniforms.uTime.value=t,q.rotation.z=.06*o,E.rotation.y=.012*o,p.position.set(1.15*Math.sin(.5*o),.5*Math.sin(o)+.25,15),p.lookAt(0,0,0),a.setRenderTarget(O),a.render(v,p),K.uniforms.tS.value=O.texture,ot(K,I),Q.uniforms.tS.value=I.texture,Q.uniforms.uAtt.value=.9,Q.uniforms.uStep.value=1.5/I.width,ot(Q,_),Q.uniforms.tS.value=_.texture,Q.uniforms.uAtt.value=.8,Q.uniforms.uStep.value=5/_.width,ot(Q,W),rt(I,_);var u,h=I;for(u=0;u<4;u++)J.uniforms.tS.value=h.texture,J.uniforms.uTx.value.set(1/h.width,1/h.height),ot(J,U[u]),rt(U[u],V[u]),h=U[u];var d=U[3];for(u=2;u>=0;u--)Y.uniforms.tS.value=d.texture,Y.uniforms.tAdd.value=U[u].texture,Y.uniforms.uTx.value.set(1/d.width,1/d.height),ot(Y,V[u]),d=V[u];Y.uniforms.tS.value=d.texture,Y.uniforms.tAdd.value=I.texture,Y.uniforms.uTx.value.set(1/d.width,1/d.height),ot(Y,G),Z.uniforms.tS.value=O.texture,Z.uniforms.tB.value=G.texture,Z.uniforms.tK.value=W.texture,Z.uniforms.tG.value=U[1].texture,ot(Z,null)},destroy:function(){w(v,[$,K,J,X,Y,R,Q,Z,A,F,g,y.material,T].concat(H,[a]))}}}});var K={};function J(t,e){var a=(e=e||{}).position||"top-right",o=K[a];o||((o=j("div","gx-toast-region")).setAttribute("data-pos",a),document.body.appendChild(o),K[a]=o);var r=j("div","gx-toast"+(e.type?" gx-toast--"+e.type:""));r.innerHTML=(e.icon?''+e.icon+"":"")+''+t+"",o.appendChild(r),requestAnimationFrame(function(){r.classList.add("is-open")});var i,n=null==e.duration?3200:e.duration;function l(){r.classList.remove("is-open"),setTimeout(function(){r.parentNode&&r.parentNode.removeChild(r)},320)}return n>0&&(i=setTimeout(l,n)),r.addEventListener("click",function(){clearTimeout(i),l()}),{close:l}}function X(t){t=t||{};var e=j("div","gx-modal-backdrop"),a=j("div","gx-modal"),o=!1!==t.closable,r="";if(o&&(r+=''),t.title&&(r+='

'+t.title+"

"),r+='
'+(t.html||t.body||"")+"
",a.innerHTML=r,t.actions&&t.actions.length){var i=j("div","gx-modal__footer");t.actions.forEach(function(t){var e=j("button","gx-btn "+(t.variant?"gx-btn--"+t.variant:"gx-btn--ghost"),t.label);e.addEventListener("click",function(){t.onClick&&t.onClick(),!1!==t.close&&n()}),i.appendChild(e)}),a.appendChild(i)}function n(){e.classList.remove("is-open"),document.removeEventListener("keydown",l),setTimeout(function(){e.parentNode&&e.parentNode.removeChild(e)},320),t.onClose&&t.onClose()}function l(t){"Escape"===t.key&&o&&n()}if(e.appendChild(a),document.body.appendChild(e),requestAnimationFrame(function(){e.classList.add("is-open")}),o){document.addEventListener("keydown",l),e.addEventListener("click",function(t){t.target===e&&n()});var s=a.querySelector(".gx-modal__close");s&&s.addEventListener("click",n)}return{close:n,el:a}}var Y=null;function Q(t){function e(){Z();var e=j("div","gx-tooltip",t.getAttribute("data-gx-tooltip"));document.body.appendChild(e);var a=t.getBoundingClientRect(),o=e.getBoundingClientRect(),r=t.getAttribute("data-gx-tooltip-pos")||"top",n=a.left+a.width/2-o.width/2,l=a.top-o.height-8;"bottom"===r&&(l=a.bottom+8),"left"===r&&(n=a.left-o.width-8,l=a.top+a.height/2-o.height/2),"right"===r&&(n=a.right+8,l=a.top+a.height/2-o.height/2),e.style.left=i(n,4,window.innerWidth-o.width-4)+"px",e.style.top=Math.max(4,l)+"px",requestAnimationFrame(function(){e.classList.add("is-open")}),Y=e}t.addEventListener("mouseenter",e),t.addEventListener("focus",e),t.addEventListener("mouseleave",Z),t.addEventListener("blur",Z)}function Z(){if(Y){var t=Y;Y=null,t.classList.remove("is-open"),setTimeout(function(){t.parentNode&&t.parentNode.removeChild(t)},180)}}function $(t){var e=t.querySelector(".gx-tabs__list");if(e){var a=[].slice.call(t.querySelectorAll(".gx-tab")),o=[].slice.call(t.querySelectorAll(".gx-tab-panel")),r=e.querySelector(".gx-tabs__indicator")||j("div","gx-tabs__indicator");r.parentNode||e.appendChild(r),a.forEach(function(t,e){t.addEventListener("click",function(){n(e)})});var i=a.findIndex(function(t){return"true"===t.getAttribute("aria-selected")});n(i<0?0:i)}function n(t){a.forEach(function(e,a){e.setAttribute("aria-selected",a===t?"true":"false"),o[a]&&(o[a].hidden=a!==t)});var e=a[t];r.style.width=e.offsetWidth+"px",r.style.transform="translateX("+e.offsetLeft+"px)"}}function tt(t){var e="multi"!==t.getAttribute("data-gx-accordion"),a=[].slice.call(t.querySelectorAll(".gx-accordion__item"));a.forEach(function(t){var o=t.querySelector(".gx-accordion__header"),r=t.querySelector(".gx-accordion__panel");o&&r&&o.addEventListener("click",function(){var o=t.classList.contains("is-open");e&&a.forEach(function(t){t.classList.remove("is-open");var e=t.querySelector(".gx-accordion__panel");e&&(e.style.maxHeight=null)}),o?(t.classList.remove("is-open"),r.style.maxHeight=null):(t.classList.add("is-open"),r.style.maxHeight=r.scrollHeight+"px")})})}function et(t){var e=t.querySelector("[data-gx-dropdown-trigger]")||t.querySelector(".gx-btn");e&&(e.addEventListener("click",function(e){e.stopPropagation(),t.classList.toggle("is-open")}),document.addEventListener("click",function(){t.classList.remove("is-open")}))}function at(t){t.addEventListener("click",function(e){var a=t.getBoundingClientRect(),o=Math.max(a.width,a.height),r=j("span","gx-ripple");r.style.width=r.style.height=o+"px",r.style.left=e.clientX-a.left-o/2+"px",r.style.top=e.clientY-a.top-o/2+"px",t.appendChild(r),setTimeout(function(){r.parentNode&&r.parentNode.removeChild(r)},650)})}function ot(t){t.addEventListener("pointermove",function(e){var a=t.getBoundingClientRect();t.style.setProperty("--gx-mx",e.clientX-a.left+"px"),t.style.setProperty("--gx-my",e.clientY-a.top+"px")})}function rt(t){if("undefined"!=typeof IntersectionObserver){var e=new IntersectionObserver(function(t){t.forEach(function(t){if(t.isIntersecting){var a=t.target.getAttribute("data-gx-reveal-delay");a&&(t.target.style.transitionDelay=a+"ms"),t.target.classList.add("is-visible"),e.unobserve(t.target)}})},{threshold:.12});t.forEach(function(t){e.observe(t)})}else t.forEach(function(t){t.classList.add("is-visible")})}function it(t){var e=[].slice.call(t.querySelectorAll(".gx-segmented__option"));function a(a){e.forEach(function(t,e){t.setAttribute("aria-checked",e===a?"true":"false");var o=t.getAttribute("data-gx-target");if(o){var r=document.querySelector(o);r&&(r.hidden=e!==a)}}),c(t,"gx:change",{index:a,value:e[a].textContent})}e.forEach(function(t,e){t.addEventListener("click",function(){a(e)})});var o=e.findIndex(function(t){return"true"===t.getAttribute("aria-checked")});e.length&&a(o<0?0:o)}function nt(t){var e=t.getAttribute("data-gx-popover-target"),a=e?document.querySelector(e):t.nextElementSibling;if(a&&a.classList.contains("gx-popover")){var o=t.parentNode;"static"===window.getComputedStyle(o).position&&(o.style.position="relative"),t.addEventListener("click",function(t){if(t.stopPropagation(),a.classList.toggle("is-open")){var e=document.querySelectorAll(".gx-popover.is-open");[].forEach.call(e,function(t){t!==a&&t.classList.remove("is-open")})}}),document.addEventListener("click",function(e){a.contains(e.target)||e.target===t||a.classList.remove("is-open")})}}function lt(t){var e=parseInt(t.getAttribute("data-gx-rating")||"5",10),a=parseInt(t.getAttribute("data-gx-value")||"0",10),o=[];t.innerHTML="";for(var r=1;r<=e;r++){var i=j("span","gx-rating__star","★");i.dataset.v=r,t.appendChild(i),o.push(i)}function n(t){o.forEach(function(e,a){e.classList.toggle("is-active",a"),r+='
'+(t.html||t.body||"")+"
",o.innerHTML=r,t.actions&&t.actions.length){var i=j("div","gx-row");t.actions.forEach(function(t){var e=j("button","gx-btn "+(t.variant?"gx-btn--"+t.variant:"gx-btn--ghost"),t.label);e.addEventListener("click",function(){t.onClick&&t.onClick(),!1!==t.close&&n()}),i.appendChild(e)}),o.appendChild(i)}function n(){a.classList.remove("is-open"),document.removeEventListener("keydown",l),setTimeout(function(){a.parentNode&&a.parentNode.removeChild(a)},320),t.onClose&&t.onClose()}function l(t){"Escape"===t.key&&n()}return a.appendChild(o),document.body.appendChild(a),requestAnimationFrame(function(){a.classList.add("is-open")}),document.addEventListener("keydown",l),a.addEventListener("click",function(t){t.target===a&&n()}),{close:n,el:o}}function ut(e){if(t)if("string"==typeof e)document.documentElement.setAttribute("data-galaxy-theme",e);else if(e&&"object"==typeof e){var a=document.documentElement;Object.keys(e).forEach(function(t){var o=0===t.indexOf("--")?t:"--gx-"+t.replace(/[A-Z]/g,function(t){return"-"+t.toLowerCase()});a.style.setProperty(o,e[t])})}}var ht=[];function dt(e){if(t){e=e||document,[].forEach.call(e.querySelectorAll("[data-galaxy]"),function(t){if(!t.__gxMounted){var e=t.getAttribute("data-galaxy");C[e]&&(t.__gxMounted=!0,ht.push(T(e,t,function(t,e){var a={};return[].forEach.call(t.attributes,function(t){if(0===t.name.indexOf(e)&&t.name!==e){var o=t.name.slice(e.length+1).replace(/-([a-z])/g,function(t,e){return e.toUpperCase()});a[o]="true"===(r=t.value)||"false"!==r&&(""===r||isNaN(Number(r))?r.indexOf(",")>=0?r.split(",").map(function(t){return t.trim()}):r:Number(r))}var r}),a}(t,"data-galaxy"))))}}),[].forEach.call(e.querySelectorAll("[data-gx-tooltip]"),Q),[].forEach.call(e.querySelectorAll("[data-gx-tabs]"),$),[].forEach.call(e.querySelectorAll("[data-gx-accordion]"),tt),[].forEach.call(e.querySelectorAll("[data-gx-dropdown]"),et),[].forEach.call(e.querySelectorAll("[data-gx-ripple], .gx-btn"),at),[].forEach.call(e.querySelectorAll(".gx-card--spotlight"),ot),[].forEach.call(e.querySelectorAll("[data-gx-segmented]"),it),[].forEach.call(e.querySelectorAll("[data-gx-popover]"),nt),[].forEach.call(e.querySelectorAll("[data-gx-rating]"),lt),[].forEach.call(e.querySelectorAll("[data-gx-copy], [data-gx-copy-target]"),st),!ct&&t&&(ct=!0,document.addEventListener("click",function(t){var e=t.target.closest&&t.target.closest(".gx-chip__close");if(e){var a=e.closest(".gx-chip");a&&a.parentNode&&a.parentNode.removeChild(a)}}));var a=[].slice.call(e.querySelectorAll(".gx-reveal, [data-gx-reveal]"));a.length&&rt(a),[].forEach.call(e.querySelectorAll("[data-gx-modal-target]"),function(t){t.__gxBound||(t.__gxBound=!0,t.addEventListener("click",function(){var e=document.querySelector(t.getAttribute("data-gx-modal-target"));X({title:t.getAttribute("data-gx-modal-title")||"",html:e?e.innerHTML:""})}))}),[].forEach.call(e.querySelectorAll("[data-gx-drawer-target]"),function(t){t.__gxBound||(t.__gxBound=!0,t.addEventListener("click",function(){var e=document.querySelector(t.getAttribute("data-gx-drawer-target"));ft({title:t.getAttribute("data-gx-drawer-title")||"",side:t.getAttribute("data-gx-drawer-side")||"right",html:e?e.innerHTML:""})}))})}}t&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",function(){dt(document)}):dt(document));var vt={version:"3.4.0",create:function(t,e,a){return T(t,e,a)},scrollScene:function(o,r){var i=a(o);if(!i)throw new Error("GalaxyJS: scrollScene target not found");var n=((r=r||{}).scenes||[]).map(function(t){return"string"==typeof t?{type:t}:t});if(!n.length)throw new Error("GalaxyJS: scrollScene needs at least one scene");var l=r.track?a(r.track):i.parentNode||i;i.classList.add("gx-scrollscene");var s=n.map(function(t,e){var a=document.createElement("div");return a.className="gx-scrollscene__layer",a.style.cssText="position:absolute;inset:0;opacity:"+(0===e?1:0)+";transition:opacity .2s linear;display:"+(e<2?"block":"none")+";",i.appendChild(a),{layer:a,ctrl:T(t.type,a,Object.assign({autoplay:!1},t.options||{})),type:t.type}}),c=n.length,f=-1,u=!1,h=!1;function d(){if(!t)return 0;var e=l.getBoundingClientRect(),a=window.innerHeight||document.documentElement.clientHeight||1,o=e.height-a;return o<=0?0:Math.max(0,Math.min(1,-e.top/o))}function v(t){for(var e=t*(c-1),a=Math.min(c-1,Math.floor(e)),o=e-a,i=0;i=.5&&aLet an AI build it for you ['hrDiagram','H-R diagram'], ['pulsarTiming','folded profile'], // v3.3 — pointer-driven & generative ['gravityWell','drag a mass'], ['nebulaPaint','paint a nebula'], ['solarWind','bow shock'], - ['starForge','seed systems'], ['relativisticJets','spin-axis jets'] + ['starForge','seed systems'], ['relativisticJets','spin-axis jets'], + // v3.4 "Optics" — the optional three.js tier (loaded on demand) + ['eventHorizon','curved-spacetime raytrace'], ['molecularCloud','3D volume + protostar'], + ['spiralForge','340k instanced stars'], ['ringedWorld','mutual ring shadows'], + ['gravitySim','GPGPU n-body'], ['starGlare','HDR bloom chain'] ]; var gallery = document.getElementById('gallery'); animals.forEach(function (a) { diff --git a/llms.txt b/llms.txt index a738943..3ef0380 100644 --- a/llms.txt +++ b/llms.txt @@ -1,8 +1,14 @@ # GalaxyJS -> Zero-dependency cosmic animation & UI component library with one unified API. -> 80 canvas & WebGL2 animations + a themeable UI kit. No build step, ~works from a single ->