Skip to content

Instantly share code, notes, and snippets.

@gkucmierz
Last active June 2, 2026 11:52
Show Gist options
  • Select an option

  • Save gkucmierz/843e36d822e2fc3f14721e835e60ab8b to your computer and use it in GitHub Desktop.

Select an option

Save gkucmierz/843e36d822e2fc3f14721e835e60ab8b to your computer and use it in GitHub Desktop.
Run this code instantly in your browser: https://instacode.app/gist/843e36d822e2fc3f14721e835e60ab8b
// Dichroic Glass X-Cube Optical Prism with Interactive Drag-to-Rotate
// Built using Three.js inside Instacode Web Worker
import { canvas, onResize, getDisplaySize, onPointerDown, onPointerMove, onPointerUp } from 'canvas';
import * as THREE from 'three';
// ==========================================
// CONFIGURATION CONSTANTS (Adjust these to customize your X-Cube!)
// ==========================================
const BG_COLOR = 0xf3f2f8; // Soft light studio background color (#f3f2f8)
const FLOOR_COLOR = 0xe5e4ec; // Light metallic floor color (#e5e4ec)
// Outer Glass Dimensions
const CUBE_SIZE = 80; // Total width/height/depth of the combined cube
// Physical Glass Settings for the 4 Segments
const GLASS_ROUGHNESS = 0.0; // 0.0 = perfect mirror polish
const GLASS_METALNESS = 0.15; // Adds metallic reflection sheen to glass edges
const GLASS_TRANSMISSION = 0.95; // Light pass-through (0.95 = 95% clear glass)
const GLASS_IOR = 1.75; // Index of refraction (higher = more edge distortion)
const GLASS_THICKNESS = 30.0; // Thickness of each segment's glass volume
const GLASS_DISPERSION = 25.0; // Chromatic dispersion (rainbow splitting along edges)
const GLASS_CLEARCOAT = 1.0; // Additional high-gloss protective layer
const GLASS_CLEARCOAT_ROUGH = 0.0; // Glossy finish for the clearcoat layer
// Dichroic Iridescence Settings (rainbow color shifting)
const IRIDESCENCE_INTENSITY = 1.0; // 0.0 = no iridescence, 1.0 = maximum dichroic effect
const IRIDESCENCE_IOR = 2.4; // Refractive index of thin-film layer (higher = more color shifts)
// Colors of the 4 individual glass segments (simulating dichroic filtering per sector)
const SEGMENT_COLORS = [
0xff0066, // Segment 1 (Left Back): Magenta/Red bias
0x00ffcc, // Segment 2 (Right Back): Cyan/Teal bias
0xffaa00, // Segment 3 (Left Front): Orange/Gold bias
0x0055ff // Segment 4 (Right Front): Deep Blue bias
];
// Internal Mirror Joints (The "X" separator planes)
const JOINT_OPACITY = 0.65; // Opacity of the intersecting dichroic planes
const JOINT_METALNESS = 0.9; // High metalness makes them act like semi-reflective mirrors
const JOINT_COLOR_1 = 0xff00bb; // Joint plane 1 color reflection (Magenta/Violet)
const JOINT_COLOR_2 = 0x00ffaa; // Joint plane 2 color reflection (Teal/Green)
// Scene Lights & Neon Elements
const SHOW_NEON_RODS = false; // Set to true to show the colorful neon rods underneath
const NEON_COLORS = [0xff0055, 0x00ffcc, 0xffcc00, 0x0055ff]; // Floor neon rods color if shown
const AMBIENT_INTENSITY = 0.9; // Ambient background fill light
const SPOT_1_INTENSITY = 12.0; // Key light highlight intensity
const SPOT_2_INTENSITY = 8.0; // Rim light intensity
const SPOT_3_INTENSITY = 8.0; // Bottom highlights intensity
// Animation & Interaction Parameters
const ROTATION_SPEED = 0.35; // Rotation speed multiplier when idle
const FLOAT_SPEED = 0.8; // Levitation speed multiplier
const FLOAT_HEIGHT = 5.0; // Floating amplitude in pixels
// ==========================================
// 1. Get Safe Initial Dimensions (preventing 0px WebGL Framebuffer crashes)
const initSize = getDisplaySize();
const initWidth = initSize.width > 0 ? initSize.width : 300;
const initHeight = initSize.height > 0 ? initSize.height : 150;
// Setup WebGL Renderer with High-Quality Settings
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: false,
powerPreference: "high-performance"
});
renderer.setSize(initWidth, initHeight, false);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.35;
// 2. Main Scene and Camera Setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(BG_COLOR);
const camera = new THREE.PerspectiveCamera(38, initWidth / initHeight, 0.1, 1000);
camera.position.set(0, 85, 235);
camera.lookAt(0, -10, 0);
// 3. Create Neon Light Rods on the Floor
const neonGroup = new THREE.Group();
const rodGeo = new THREE.CylinderGeometry(2.5, 2.5, 180, 8);
for (let i = 0; i < 4; i++) {
const rodMat = new THREE.MeshBasicMaterial({ color: NEON_COLORS[i] });
const rod = new THREE.Mesh(rodGeo, rodMat);
rod.rotation.z = Math.PI / 2; // Horizontal alignment
rod.position.set(0, -42, (i - 1.5) * 35); // Space them out on the floor
neonGroup.add(rod);
}
if (SHOW_NEON_RODS) {
scene.add(neonGroup);
}
// 4. Floor Plane (Light reflective surface)
const floorGeo = new THREE.PlaneGeometry(400, 400);
const floorMat = new THREE.MeshStandardMaterial({
color: FLOOR_COLOR,
roughness: 0.15,
metalness: 0.6
});
const floor = new THREE.Mesh(floorGeo, floorMat);
floor.rotation.x = -Math.PI / 2;
floor.position.y = -44; // Just underneath the neon rods
scene.add(floor);
// 5. Build the Dichroic X-Cube (4 Glass Segments + Internal Mirror Joints)
const cubeGroup = new THREE.Group();
const halfSize = CUBE_SIZE / 2;
const segGeo = new THREE.BoxGeometry(halfSize, CUBE_SIZE, halfSize);
// Offset positions for the 4 quadrants
const offsets = [
{ x: -halfSize / 2, z: -halfSize / 2, name: 'left-back' },
{ x: halfSize / 2, z: -halfSize / 2, name: 'right-back' },
{ x: -halfSize / 2, z: halfSize / 2, name: 'left-front' },
{ x: halfSize / 2, z: halfSize / 2, name: 'right-front' }
];
// Create the 4 glass blocks representing the quadrants of the X-Cube
offsets.forEach((offset, idx) => {
const segMat = new THREE.MeshPhysicalMaterial({
color: SEGMENT_COLORS[idx],
roughness: GLASS_ROUGHNESS,
metalness: GLASS_METALNESS,
transmission: GLASS_TRANSMISSION,
ior: GLASS_IOR,
thickness: GLASS_THICKNESS,
dispersion: GLASS_DISPERSION,
clearcoat: GLASS_CLEARCOAT,
clearcoatRoughness: GLASS_CLEARCOAT_ROUGH,
opacity: 1.0,
transparent: true,
side: THREE.DoubleSide,
iridescence: IRIDESCENCE_INTENSITY,
iridescenceIOR: IRIDESCENCE_IOR,
iridescenceThicknessRange: [200 + idx * 100, 500 + idx * 100] // Shift colors slightly for each block
});
const block = new THREE.Mesh(segGeo, segMat);
block.position.set(offset.x, 0, offset.z);
cubeGroup.add(block);
});
// C. Intersecting Semi-Reflective Joint Planes (The "X" separator)
const planeGeo = new THREE.PlaneGeometry(CUBE_SIZE - 0.2, CUBE_SIZE - 0.2);
const jointMat1 = new THREE.MeshStandardMaterial({
color: JOINT_COLOR_1,
roughness: 0.0,
metalness: JOINT_METALNESS,
transparent: true,
opacity: JOINT_OPACITY,
side: THREE.DoubleSide,
depthWrite: false
});
const jointPlane1 = new THREE.Mesh(planeGeo, jointMat1);
cubeGroup.add(jointPlane1);
const jointMat2 = new THREE.MeshStandardMaterial({
color: JOINT_COLOR_2,
roughness: 0.0,
metalness: JOINT_METALNESS,
transparent: true,
opacity: JOINT_OPACITY,
side: THREE.DoubleSide,
depthWrite: false
});
const jointPlane2 = new THREE.Mesh(planeGeo, jointMat2);
jointPlane2.rotation.y = Math.PI / 2; // Perpendicular to jointPlane1
cubeGroup.add(jointPlane2);
scene.add(cubeGroup);
// 6. Direct Lighting (No environment maps to bypass risky WebGL blit mipmap worker failures)
const ambientLight = new THREE.AmbientLight(0xffffff, AMBIENT_INTENSITY);
scene.add(ambientLight);
// Key light from top-right-front
const spotLight1 = new THREE.SpotLight(0xffffff, SPOT_1_INTENSITY, 400, Math.PI / 4, 0.5, 1.0);
spotLight1.position.set(150, 200, 150);
scene.add(spotLight1);
// Fill light from left-back
const spotLight2 = new THREE.SpotLight(0xffffff, SPOT_2_INTENSITY, 400, Math.PI / 4, 0.5, 1.0);
spotLight2.position.set(-150, 150, -150);
scene.add(spotLight2);
// Highlight light from bottom-front to lift up floor shadows
const spotLight3 = new THREE.SpotLight(0xffffff, SPOT_3_INTENSITY, 300, Math.PI / 3, 0.5, 1.0);
spotLight3.position.set(0, -100, 150);
scene.add(spotLight3);
// Handle resizing safely
onResize((w, h) => {
if (w > 0 && h > 0) {
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
});
// Interactive Drag-to-Rotate logic
let isDragging = false;
let previousPointerPos = { x: 0, y: 0 };
const rotationTarget = { x: 0.15, y: 0.0 };
onPointerDown(e => {
isDragging = true;
previousPointerPos = { x: e.x, y: e.y };
});
onPointerMove(e => {
if (!isDragging) return;
const deltaX = e.x - previousPointerPos.x;
const deltaY = e.y - previousPointerPos.y;
rotationTarget.y += deltaX * 0.007;
rotationTarget.x += deltaY * 0.007;
previousPointerPos = { x: e.x, y: e.y };
});
onPointerUp(() => {
isDragging = false;
});
// 7. Animation Loop
let time = 0;
function animate() {
requestAnimationFrame(animate);
// Skip rendering if the canvas width/height is zero (which auto-syncs with DOM layout)
if (canvas.width === 0 || canvas.height === 0) {
return;
}
time += 0.007;
if (isDragging) {
// Smoothly interpolate rotation to drag target
cubeGroup.rotation.y = THREE.MathUtils.lerp(cubeGroup.rotation.y, rotationTarget.y, 0.15);
cubeGroup.rotation.x = THREE.MathUtils.lerp(cubeGroup.rotation.x, rotationTarget.x, 0.15);
} else {
// Slowly rotate when idle
cubeGroup.rotation.y += 0.0025 * ROTATION_SPEED;
rotationTarget.y = cubeGroup.rotation.y;
rotationTarget.x = cubeGroup.rotation.x;
}
// Keep Z rotation stable
cubeGroup.rotation.z = Math.cos(time * 0.05) * 0.02;
// Float above the floor to show off under-glass light refraction
cubeGroup.position.y = Math.sin(time * FLOAT_SPEED) * FLOAT_HEIGHT;
renderer.render(scene, camera);
}
animate();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment