Open in Lab
MCP
threejs postprocessing webgl
Targets: JS HTML
Code
:root {
--bg: #03050c;
--text: #edf4ff;
--muted: #c2d1e8;
--accent: #90e8ff;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: "Avenir Next", "Segoe UI", sans-serif;
}
#scene {
position: fixed;
inset: 0;
}
.topbar {
position: fixed;
inset: 0 0 auto 0;
z-index: 20;
display: flex;
justify-content: space-between;
align-items: center;
padding: .75rem 1rem;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
}
.topbar a {
color: var(--accent);
text-decoration: none;
font-weight: 700;
}
button {
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 999px;
padding: .45rem .8rem;
color: var(--text);
background: rgba(255, 255, 255, 0.07);
cursor: pointer;
}
.overlay {
position: relative;
z-index: 10;
min-height: 100vh;
width: min(900px, 92%);
margin: 0 auto;
display: grid;
align-content: center;
text-align: center;
gap: .7rem;
}
.label {
margin: 0;
color: var(--accent);
text-transform: uppercase;
letter-spacing: .1em;
}
h1,
p {
margin: 0;
}
h1 {
font-size: clamp(2rem, 7vw, 4rem);
}
p {
color: var(--muted);
}if (!window.MotionPreference) {
const __mql = window.matchMedia("(prefers-reduced-motion: reduce)");
const __listeners = new Set();
const MotionPreference = {
prefersReducedMotion() {
return __mql.matches;
},
setOverride(value) {
const reduced = Boolean(value);
document.documentElement.classList.toggle("reduced-motion", reduced);
window.dispatchEvent(new CustomEvent("motion-preference", { detail: { reduced } }));
for (const listener of __listeners) {
try {
listener({ reduced, override: reduced, systemReduced: __mql.matches });
} catch {}
}
},
onChange(listener) {
__listeners.add(listener);
try {
listener({
reduced: __mql.matches,
override: null,
systemReduced: __mql.matches,
});
} catch {}
return () => __listeners.delete(listener);
},
getState() {
return { reduced: __mql.matches, override: null, systemReduced: __mql.matches };
},
};
window.MotionPreference = MotionPreference;
}
import * as THREE from "three";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
const host = document.getElementById("scene");
const toggle = document.getElementById("toggleMotion");
let motionEnabled = !window.MotionPreference.prefersReducedMotion();
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x02030a);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 2, 11);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
host.appendChild(renderer.domElement);
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloom = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
0.9,
0.5,
0.15
);
composer.addPass(bloom);
const group = new THREE.Group();
scene.add(group);
const colors = [0x7ed7ff, 0xf3e8b8, 0xbb83ff, 0xf962d8];
for (let i = 0; i < 24; i += 1) {
const geometry =
i % 2
? new THREE.IcosahedronGeometry(0.25 + Math.random() * 0.3, 1)
: new THREE.SphereGeometry(0.23 + Math.random() * 0.25, 22, 22);
const material = new THREE.MeshStandardMaterial({
color: colors[i % colors.length],
emissive: colors[i % colors.length],
emissiveIntensity: 0.45,
metalness: 0.25,
roughness: 0.35,
});
const mesh = new THREE.Mesh(geometry, material);
const angle = (i / 24) * Math.PI * 2;
const radius = 3 + (i % 5) * 0.55;
mesh.position.set(Math.cos(angle) * radius, ((i % 4) - 1.5) * 0.9, Math.sin(angle) * radius);
mesh.userData = { angle, radius, speed: 0.15 + (i % 5) * 0.035 };
group.add(mesh);
}
scene.add(new THREE.AmbientLight(0x9db9ff, 0.45));
const key = new THREE.PointLight(0x78d4ff, 1.3, 45);
key.position.set(6, 7, 7);
scene.add(key);
function setLabel() {
toggle.textContent = motionEnabled ? "Disable motion" : "Enable motion";
}
function animate(time) {
requestAnimationFrame(animate);
if (motionEnabled) {
const t = time * 0.001;
group.rotation.y += 0.002;
group.children.forEach((mesh) => {
mesh.userData.angle += mesh.userData.speed * 0.002;
mesh.position.x = Math.cos(mesh.userData.angle + t * 0.5) * mesh.userData.radius;
mesh.position.z = Math.sin(mesh.userData.angle + t * 0.5) * mesh.userData.radius;
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.008;
});
}
composer.render();
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
}
toggle.addEventListener("click", () => {
motionEnabled = !motionEnabled;
setLabel();
});
window.addEventListener("resize", onResize);
setLabel();
animate(0);<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo 11 - Orbit + Bloom</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="topbar">
<a href="../">Back to demos</a>
<button id="toggleMotion"></button>
</header>
<div id="scene"></div>
<section class="overlay">
<p class="label">Demo 11</p>
<h1>Orbiting Objects + Bloom</h1>
<p>A composited glow scene for depth and spectacle.</p>
</section>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.164.1/build/three.module.js"
}
}
</script>
<script type="module" src="script.js"></script>
</body>
</html>Three.js Orbit + Bloom
Orbiting object field rendered with bloom compositing.
Source
- Repository:
libs-gen - Original demo id:
11-threejs-orbit-bloom
Notes
Orbiting object field rendered with bloom compositing.