Skip to content

Instantly share code, notes, and snippets.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Rotating Letter Sphere</title>
<style>
body {
margin: 0;
padding: 0;
background-color: black;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
color: white;
font-family: monospace;
}
canvas {
display: block;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
// Constants
const LETTERS = "abcdefghijklmnopqrstuvwxyz";
const POINT_COUNT = 1000;
const SPHERE_RADIUS = 200;
const ROTATION_SPEED_X = 0.005;
const ROTATION_SPEED_Y = 0.003;
// Setup canvas
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Set canvas size
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
// Initialize
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
// Generate random points on sphere surface
const points = [];
for (let i = 0; i < POINT_COUNT; i++) {
// Use spherical coordinates to distribute points evenly
const phi = Math.acos(2 * Math.random() - 1);
const theta = 2 * Math.PI * Math.random();
// Convert to Cartesian coordinates
const x = SPHERE_RADIUS * Math.sin(phi) * Math.cos(theta);
const y = SPHERE_RADIUS * Math.sin(phi) * Math.sin(theta);
const z = SPHERE_RADIUS * Math.cos(phi);
// Select random letter
const letterIndex = Math.floor(Math.random() * LETTERS.length);
const letter = LETTERS[letterIndex];
points.push({ x, y, z, letter });
}
// Rotation state
let angleX = 0;
let angleY = 0;
// Animation function
function animate() {
// Update rotation
angleX += ROTATION_SPEED_X;
angleY += ROTATION_SPEED_Y;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Sort points by z-value for correct depth rendering
const rotatedPoints = points.map((point) => {
// Apply rotation around Y axis
const sinY = Math.sin(angleY);
const cosY = Math.cos(angleY);
const x1 = point.x * cosY - point.z * sinY;
const z1 = point.z * cosY + point.x * sinY;
// Apply rotation around X axis
const sinX = Math.sin(angleX);
const cosX = Math.cos(angleX);
const y2 = point.y * cosX - z1 * sinX;
const z2 = z1 * cosX + point.y * sinX;
return {
x: x1,
y: y2,
z: z2,
letter: point.letter,
};
});
// Sort by z for proper depth rendering (furthest first)
rotatedPoints.sort((a, b) => b.z - a.z);
// Render points
rotatedPoints.forEach((point) => {
// Calculate screen position
const scale = 1000 / (1000 + point.z); // Perspective division
const screenX = canvas.width / 2 + point.x * scale;
const screenY = canvas.height / 2 + point.y * scale;
const colorValue = Math.round(
(255 * (point.z + SPHERE_RADIUS)) / (2 * SPHERE_RADIUS),
);
const color = `rgb(${colorValue},${colorValue},${colorValue})`;
// Draw letter
ctx.font = `${Math.max(8, 16 * scale)}px monospace`;
ctx.fillStyle = color;
ctx.fillText(point.letter, screenX, screenY);
});
requestAnimationFrame(animate);
}
// Start animation
animate();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment