Spooky Spectral Ghost
A spectral ghost following your cursor, with glowing eyes, particles, fireflies, and an analog VHS-style decay effect. Includes a spooky preloader and quote.
- Built with Three.js + Tweakpane
- Custom preloader & ghost shader
- GPU-accelerated WebGL canvas
<!-- Spooky preloader -->
<div id="preloader" class="preloader">
<div class="preloader-content">
<div class="ghost-loader">
<svg class="ghost-svg" viewBox="0 0 512 512">
<path class="ghost-body" d="..." />
<circle class="ghost-eye left-eye" cx="208" cy="225" r="22" />
<circle class="ghost-eye right-eye" ... />
</svg>
</div>
<div class="loading-text">Summoning spirits</div>
</div>
</div>
<!-- Quote + ghost canvas -->
<div id="main-content" class="content">
<h1 class="quote">
Veil of Dust<br />Trail of Ash<br />Heart of Ice
</h1>
<span class="author">
Whispers through memory
</span>
</div>
@import url("https://fonts.googleapis.com/css2?family=Boldonse&display=swap");
html, body {
margin: 0;
height: 100%;
background: #111;
color: #e0e0e0;
}
.preloader {
position: fixed;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #0a0a0a, #1a1a1a);
transition: opacity 1s ease-out;
}
.preloader.fade-out {
opacity: 0;
pointer-events: none;
}
.content {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 20px;
opacity: 0;
transition: opacity 1.5s ease-in;
}
.content.fade-in {
opacity: 1;
}
.quote {
font-family: "Boldonse", system-ui;
font-size: clamp(2.2rem, 5vw, 4.8rem);
line-height: 1.15;
text-transform: uppercase;
}
canvas {
opacity: 0 !important;
transition: opacity 2s ease-in;
}
canvas.fade-in {
opacity: 1 !important;
}
// Import Three.js + post-processing utilities
import * as THREE from "https://esm.sh/three";
import { Pane } from "https://cdn.skypack.dev/tweakpane@4.0.4";
// + EffectComposer, RenderPass, UnrealBloomPass, ShaderPass...
// 1) Preloader controller (updates progress bar + fade in/out)
class PreloaderManager {
constructor() { ... }
updateProgress(step) { ... }
complete(canvas) { ... }
}
// 2) Three.js scene: ghost mesh, fireflies, particles, atmosphere plane
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(...);
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
// 3) Post-processing: bloom + custom analogDecay shader
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
const bloomPass = new UnrealBloomPass(...);
const analogPass = new ShaderPass(analogDecayShader);
// 4) Ghost group + glowing eyes + fireflies + particle trail
const ghostGroup = new THREE.Group();
const ghostBody = new THREE.Mesh(ghostGeometry, ghostMaterial);
ghostGroup.add(ghostBody);
// createEyes(), createFireflies(), createParticlePool()...
// 5) Mouse tracking makes the ghost follow your cursor
window.addEventListener("mousemove", (event) => {
// convert mouse position into scene coordinates
...
});
// 6) Animation loop: update ghost, eyes, fireflies, particles, shaders
function animate(time) {
requestAnimationFrame(animate);
// easing, wobble, scale, analog noise & scanlines
composer.render();
}
animate(0);