Custom Code Experiment •
Vanta JS - Rings
Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vanta JS - Rings</title>
<!-- Three.js (required by Vanta) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<!-- Vanta.js core -->
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.rings.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #000000;
}
/* ---- Vanta layer ---- */
#vantajs {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
filter: none;
transition: filter 0.6s ease;
}
body.retro #vantajs {
filter: grayscale(1) sepia(1) hue-rotate(75deg) saturate(6) brightness(0.9) contrast(1.25);
}
/* ---- CRT overlay group (hidden in normal mode) ---- */
.crt-overlay-group {
position: fixed;
inset: 0;
pointer-events: none;
opacity: 0;
transition: opacity 0.6s ease;
}
body.retro .crt-overlay-group {
opacity: 1;
}
.grain {
position: fixed;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
opacity: 0.05;
mix-blend-mode: overlay;
animation: grainMove 0.5s steps(3) infinite;
}
.scanlines {
position: fixed;
inset: 0;
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0px,
rgba(0, 0, 0, 0) 1px,
rgba(0, 0, 0, 0.35) 2px,
rgba(0, 0, 0, 0.35) 3px
);
mix-blend-mode: multiply;
}
.vignette {
position: fixed;
inset: 0;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.7) 100%);
}
.crt-flicker {
position: fixed;
inset: 0;
animation: flicker 6s infinite;
}
@keyframes grainMove {
0% { transform: translate(0, 0); }
33% { transform: translate(-4%, 5%); }
66% { transform: translate(3%, -4%); }
100% { transform: translate(0, 0); }
}
@keyframes flicker {
0%, 100% { background: rgba(0, 0, 0, 0); }
92% { background: rgba(0, 0, 0, 0); }
93% { background: rgba(120, 255, 160, 0.05); }
94% { background: rgba(0, 0, 0, 0.08); }
95% { background: rgba(120, 255, 160, 0.03); }
96% { background: rgba(0, 0, 0, 0); }
}
/* ---- toggle button ---- */
#toggle-btn {
position: fixed;
bottom: 24px;
right: 24px;
z-index: 20;
padding: 10px 16px;
font-family: 'Courier New', monospace;
font-size: 12px;
letter-spacing: 0.06em;
text-transform: uppercase;
border-radius: 4px;
cursor: pointer;
background: rgba(255, 255, 255, 0.15);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.35);
backdrop-filter: blur(4px);
transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
#toggle-btn:hover {
background: rgba(255, 255, 255, 0.28);
}
body.retro #toggle-btn {
background: rgba(20, 40, 25, 0.55);
color: #8affa0;
border-color: #2f6b45;
}
body.retro #toggle-btn:hover {
background: rgba(30, 60, 38, 0.75);
}
</style>
</head>
<body>
<div id="vantajs"></div>
<div class="crt-overlay-group">
<div class="grain"></div>
<div class="scanlines"></div>
<div class="vignette"></div>
<div class="crt-flicker"></div>
</div>
<button id="toggle-btn">Switch to Retroscope</button>
<script>
document.addEventListener('DOMContentLoaded', function () {
if (typeof VANTA === 'undefined' || !VANTA.RINGS) {
console.warn('VANTA.RINGS not available — check script loading.');
return;
}
// Base rendering config — unchanged between modes.
var effect = VANTA.RINGS({
el: "#vantajs",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00
});
// Snapshot Vanta's own defaults so "Switch to Normal" reverts
// exactly, rather than guessing them.
var NORMAL_OPTIONS = {
color: effect.options.color,
backgroundColor: effect.options.backgroundColor
};
var RETRO_OPTIONS = {
color: 0x3fdc7a,
backgroundColor: 0x02110a
};
var isRetro = false;
var btn = document.getElementById('toggle-btn');
btn.addEventListener('click', function () {
isRetro = !isRetro;
document.body.classList.toggle('retro', isRetro);
effect.setOptions(isRetro ? RETRO_OPTIONS : NORMAL_OPTIONS);
btn.textContent = isRetro ? 'Switch to Normal' : 'Switch to Retroscope';
});
});
</script>
</body>
</html>