Skip to content

Instantly share code, notes, and snippets.

@gkucmierz
Created June 2, 2026 08:15
Show Gist options
  • Select an option

  • Save gkucmierz/5aecb8cd847adba05b8a7c42466f9e3f to your computer and use it in GitHub Desktop.

Select an option

Save gkucmierz/5aecb8cd847adba05b8a7c42466f9e3f to your computer and use it in GitHub Desktop.
Run this code instantly in your browser: https://instacode.app/gist/5aecb8cd847adba05b8a7c42466f9e3f
// Glassmorphism 3D Rotating Cube for Instacode
// Features: Dual-cube perspective engine, global depth sorting, Phong lighting, and specular glass gradients
import { canvas, getContext, onResize } from 'canvas';
const ctx = getContext('2d');
let width = 600;
let height = 400;
// Handle canvas resizing
onResize((w, h) => {
width = w;
height = h;
canvas.width = w;
canvas.height = h;
});
// Setup 3D constants
const CAMERA_DIST = 500;
const FOV = 450;
// Cube builder function
const createCube = (size) => {
const half = size / 2;
return {
vertices: [
{x: -half, y: -half, z: -half}, // 0
{x: half, y: -half, z: -half}, // 1
{x: half, y: half, z: -half}, // 2
{x: -half, y: half, z: -half}, // 3
{x: -half, y: -half, z: half}, // 4
{x: half, y: -half, z: half}, // 5
{x: half, y: half, z: half}, // 6
{x: -half, y: half, z: half} // 7
],
faces: [
{ indices: [4, 5, 6, 7], name: 'front' },
{ indices: [1, 0, 3, 2], name: 'back' },
{ indices: [0, 1, 5, 4], name: 'top' },
{ indices: [7, 6, 2, 3], name: 'bottom' },
{ indices: [0, 4, 7, 3], name: 'left' },
{ indices: [5, 1, 2, 6], name: 'right' }
]
};
};
const outerCube = createCube(220);
const innerCube = createCube(110);
let angleX = 0.5;
let angleY = 0.5;
let angleZ = 0.2;
// Light source coordinates in 3D space
const lightSource = { x: 300, y: -300, z: -200 };
function rotateX(point, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return {
x: point.x,
y: point.y * cos - point.z * sin,
z: point.y * sin + point.z * cos
};
}
function rotateY(point, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return {
x: point.x * cos - point.z * sin,
y: point.y,
z: point.x * sin + point.z * cos
};
}
function rotateZ(point, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return {
x: point.x * cos - point.y * sin,
y: point.x * sin + point.y * cos,
z: point.z
};
}
function project(point) {
const scale = FOV / (point.z + CAMERA_DIST);
return {
x: point.x * scale + width / 2,
y: point.y * scale + height / 2,
z: point.z
};
}
// Compute normal vector for backface culling and lighting
function getNormal(p0, p1, p2) {
const v1 = { x: p1.x - p0.x, y: p1.y - p0.y, z: p1.z - p0.z };
const v2 = { x: p2.x - p0.x, y: p2.y - p0.y, z: p2.z - p0.z };
return {
x: v1.y * v2.z - v1.z * v2.y,
y: v1.z * v2.x - v1.x * v2.z,
z: v1.x * v2.y - v1.y * v2.x
};
}
function normalize(v) {
const len = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
return { x: v.x / len, y: v.y / len, z: v.z / len };
}
function dot(v1, v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
function draw() {
// Update rotation angles
angleX += 0.007;
angleY += 0.011;
angleZ += 0.005;
// Clear canvas with deep space radial gradient background
const bgGrad = ctx.createRadialGradient(width / 2, height / 2, 50, width / 2, height / 2, Math.max(width, height));
bgGrad.addColorStop(0, '#1a1829'); // Deep dark violet
bgGrad.addColorStop(0.5, '#0b0a12'); // Dark space blue
bgGrad.addColorStop(1, '#020204'); // Absolute black
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, width, height);
// Background stars/dust particle system
ctx.fillStyle = 'rgba(122, 206, 215, 0.1)';
for (let i = 0; i < 30; i++) {
const x = (Math.sin(i * 99 + angleX * 0.2) * 0.5 + 0.5) * width;
const y = (Math.cos(i * 33 + angleY * 0.2) * 0.5 + 0.5) * height;
ctx.beginPath();
ctx.arc(x, y, 1.5, 0, Math.PI * 2);
ctx.fill();
}
// Pre-process cube geometry
const prepareCubeGeometry = (cube, angX, angY, angZ) => {
const rotated = cube.vertices.map(v => {
let p = rotateX(v, angX);
p = rotateY(p, angY);
p = rotateZ(p, angZ);
return p;
});
const projected = rotated.map(v => project(v));
const facesData = cube.faces.map(face => {
const p = face.indices.map(idx => projected[idx]);
const r = face.indices.map(idx => rotated[idx]);
const avgZ = (r[0].z + r[1].z + r[2].z + r[3].z) / 4;
const rawNormal = getNormal(r[0], r[1], r[2]);
const normal = normalize(rawNormal);
const center3D = {
x: (r[0].x + r[1].x + r[2].x + r[3].x) / 4,
y: (r[0].y + r[1].y + r[2].y + r[3].y) / 4,
z: (r[0].z + r[1].z + r[2].z + r[3].z) / 4
};
const lightVec = normalize({
x: lightSource.x - center3D.x,
y: lightSource.y - center3D.y,
z: lightSource.z - center3D.z
});
const viewVec = normalize({
x: center3D.x,
y: center3D.y,
z: center3D.z + CAMERA_DIST
});
const diffuse = Math.max(0, dot(normal, lightVec));
const reflectVec = {
x: 2 * dot(normal, lightVec) * normal.x - lightVec.x,
y: 2 * dot(normal, lightVec) * normal.y - lightVec.y,
z: 2 * dot(normal, lightVec) * normal.z - lightVec.z
};
const specular = Math.pow(Math.max(0, dot(reflectVec, viewVec)), 8);
const isFront = dot(normal, viewVec) < 0;
return {
points: p,
avgZ,
diffuse,
specular,
isFront,
name: face.name
};
});
return facesData;
};
// Outer cube (glass shell) and Inner cube (glowing magenta core)
const outerFaces = prepareCubeGeometry(outerCube, angleX, angleY, angleZ);
const innerFaces = prepareCubeGeometry(innerCube, -angleX * 1.2, -angleY * 0.8, angleZ * 1.5);
const allRenderableFaces = [];
outerFaces.forEach(f => allRenderableFaces.push({ ...f, isOuter: true }));
innerFaces.forEach(f => allRenderableFaces.push({ ...f, isOuter: false }));
// Global depth sorting (Painters Algorithm)
allRenderableFaces.sort((a, b) => b.avgZ - a.avgZ);
// Render faces from back to front
allRenderableFaces.forEach(face => {
const pts = face.points;
ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y);
for (let i = 1; i < pts.length; i++) {
ctx.lineTo(pts[i].x, pts[i].y);
}
ctx.closePath();
if (face.isOuter) {
// --- OUTER GLASS SHELL ---
if (!face.isFront) {
// Back faces of the outer glass cube (subtle lines and low opacity fill)
ctx.fillStyle = 'rgba(255, 255, 255, 0.02)';
ctx.fill();
ctx.strokeStyle = 'rgba(122, 206, 215, 0.15)';
ctx.lineWidth = 1;
ctx.stroke();
} else {
// Front faces of the outer glass cube (specular highlights, gloss gradient)
const grad = ctx.createLinearGradient(pts[0].x, pts[0].y, pts[2].x, pts[2].y);
const opacity = 0.08 + face.specular * 0.15;
const colorCyan = `rgba(122, 206, 215, ${opacity})`;
const colorMagenta = `rgba(235, 94, 155, ${opacity * 0.5})`;
const specColor = `rgba(255, 255, 255, ${0.1 + face.specular * 0.4})`;
grad.addColorStop(0, specColor);
grad.addColorStop(0.3, colorCyan);
grad.addColorStop(1, colorMagenta);
ctx.fillStyle = grad;
ctx.fill();
// Neon glossy borders
ctx.strokeStyle = `rgba(255, 255, 255, ${0.5 + face.diffuse * 0.3})`;
ctx.lineWidth = 2.0;
ctx.shadowColor = '#7aced7';
ctx.shadowBlur = 12;
ctx.stroke();
ctx.shadowBlur = 0; // Reset blur immediately
// Inner bezel highlight (creates thickness illusion)
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
ctx.lineWidth = 1;
ctx.stroke();
}
} else {
// --- INNER NEON CORE ---
if (!face.isFront) {
// Back faces of the inner core
ctx.fillStyle = 'rgba(235, 94, 155, 0.04)';
ctx.fill();
ctx.strokeStyle = 'rgba(235, 94, 155, 0.15)';
ctx.lineWidth = 0.5;
ctx.stroke();
} else {
// Front faces of the inner core (bright glowing pink)
const grad = ctx.createLinearGradient(pts[1].x, pts[1].y, pts[3].x, pts[3].y);
grad.addColorStop(0, 'rgba(235, 94, 155, 0.25)'); // Glowing hot pink
grad.addColorStop(1, 'rgba(122, 206, 215, 0.05)');
ctx.fillStyle = grad;
ctx.fill();
ctx.strokeStyle = `rgba(235, 94, 155, ${0.7 + face.diffuse * 0.3})`;
ctx.lineWidth = 1.5;
ctx.shadowColor = '#eb5e9b';
ctx.shadowBlur = 8;
ctx.stroke();
ctx.shadowBlur = 0;
}
}
});
// Draw HUD data overlay
ctx.font = '11px monospace';
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
ctx.fillText('CORE: ACTIVE [ROTATION3D]', 20, 30);
ctx.fillStyle = '#7aced7';
ctx.fillText('GLASSMORPHISM SHADER: ON', 20, 45);
ctx.fillStyle = '#eb5e9b';
ctx.fillText(`CAM DIST: ${CAMERA_DIST}px`, 20, 60);
requestAnimationFrame(draw);
}
// Start rendering
draw();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment