mobius / index.html
etnom's picture
Add 2 files
1d03a64 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Metallic Infinity Möbius Strip</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
}
#info {
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
color: white;
font-family: 'Arial', sans-serif;
pointer-events: none;
text-shadow: 0 0 5px rgba(0,0,0,0.5);
}
.title {
position: absolute;
top: 20px;
width: 100%;
text-align: center;
color: white;
font-family: 'Arial', sans-serif;
font-size: 2.5rem;
font-weight: bold;
text-shadow: 0 0 15px rgba(255, 215, 0, 0.8);
}
.controls {
position: absolute;
top: 80px;
right: 20px;
background: rgba(0,0,0,0.5);
padding: 15px;
border-radius: 10px;
color: white;
font-family: 'Arial', sans-serif;
z-index: 100;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 215, 0, 0.3);
}
.control-group {
margin-bottom: 12px;
}
label {
display: block;
margin-bottom: 5px;
color: #ffd700;
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 6px;
background: rgba(255, 215, 0, 0.2);
border-radius: 3px;
outline: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: #ffd700;
border-radius: 50%;
cursor: pointer;
}
button {
transition: all 0.3s ease;
}
</style>
</head>
<body>
<div class="title">Metallic Infinity Möbius Strip</div>
<div class="controls">
<div class="control-group">
<label for="rotationSpeed">Rotation Speed</label>
<input type="range" id="rotationSpeed" min="0" max="0.02" step="0.001" value="0.005">
</div>
<div class="control-group">
<label class="flex items-center">
<input type="checkbox" id="wireframe" class="mr-2 h-4 w-4 accent-yellow-400">
<span>Show Wireframe</span>
</label>
</div>
<div class="flex space-x-2 mt-4">
<button id="pauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white px-4 py-2 rounded-lg font-medium shadow-md hover:shadow-lg transform hover:-translate-y-0.5 transition-all">
Pause
</button>
</div>
</div>
<div id="info">Drag to rotate | Scroll to zoom</div>
<script>
// Initialize Three.js scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// Create Infinity Möbius strip geometry
const createInfinityMobiusStrip = (radius = 1, tubeRadius = 0.3, radialSegments = 128, tubularSegments = 128) => {
const geometry = new THREE.BufferGeometry();
const vertices = [];
const indices = [];
const normals = [];
const uvs = [];
for (let i = 0; i <= radialSegments; i++) {
const v = i / radialSegments * Math.PI * 2;
for (let j = 0; j <= tubularSegments; j++) {
const u = j / tubularSegments * Math.PI * 4;
// Infinity shape parametric equations (lemniscate of Bernoulli) with Möbius twist
const scale = 1.5;
const x = scale * Math.cos(u) / (1 + Math.sin(u)*Math.sin(u));
const y = scale * Math.sin(u) * Math.cos(u) / (1 + Math.sin(u)*Math.sin(u));
// Add tube offset with Möbius twist
const nx = x + tubeRadius * Math.cos(v/2) * Math.cos(u);
const ny = y + tubeRadius * Math.cos(v/2) * Math.sin(u);
const nz = tubeRadius * Math.sin(v/2);
vertices.push(nx, ny, nz);
// Calculate normals
const normal = new THREE.Vector3(
Math.cos(u) * Math.cos(v/2),
Math.sin(u) * Math.cos(v/2),
Math.sin(v/2)
).normalize();
normals.push(normal.x, normal.y, normal.z);
uvs.push(j / tubularSegments, i / radialSegments);
}
}
// Create indices
for (let i = 0; i < radialSegments; i++) {
for (let j = 0; j < tubularSegments; j++) {
const a = i * (tubularSegments + 1) + j;
const b = a + tubularSegments + 1;
const c = a + 1;
const d = b + 1;
indices.push(a, b, d);
indices.push(a, d, c);
}
}
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
return geometry;
};
// Create ultra-metallic gold material
const material = new THREE.MeshPhysicalMaterial({
color: 0xffd700, // Gold color
metalness: 1.0,
roughness: 0.1,
clearcoat: 1.0,
clearcoatRoughness: 0.05,
ior: 2.5,
specularIntensity: 1.5,
envMapIntensity: 2.0,
emissive: 0xffcc00,
emissiveIntensity: 0.2,
side: THREE.DoubleSide,
});
const wireframeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: true,
transparent: true,
opacity: 0.3
});
// Create Infinity Möbius strip mesh
const mobiusStrip = new THREE.Mesh(createInfinityMobiusStrip(2, 0.4, 128, 128), material);
scene.add(mobiusStrip);
mobiusStrip.rotation.x = Math.PI / 4;
mobiusStrip.rotation.y = Math.PI / 4;
// Add wireframe version
const wireframe = new THREE.Mesh(mobiusStrip.geometry, wireframeMaterial);
scene.add(wireframe);
wireframe.visible = false;
// Add lights for dramatic metallic appearance
const ambientLight = new THREE.AmbientLight(0x404040, 0.3);
scene.add(ambientLight);
// Main directional light
const directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight1.position.set(2, 2, 2);
directionalLight1.castShadow = true;
directionalLight1.shadow.mapSize.width = 2048;
directionalLight1.shadow.mapSize.height = 2048;
scene.add(directionalLight1);
// Secondary directional light for contrast
const directionalLight2 = new THREE.DirectionalLight(0xff9d00, 0.6);
directionalLight2.position.set(-1, -1, -1);
scene.add(directionalLight2);
// Point light to enhance metallic reflections
const pointLight = new THREE.PointLight(0xffcc00, 2, 15);
pointLight.position.set(3, 5, 0);
pointLight.castShadow = true;
scene.add(pointLight);
// Add another point light for balance
const pointLight2 = new THREE.PointLight(0xff6600, 1.5, 15);
pointLight2.position.set(-3, -5, 0);
scene.add(pointLight2);
// Camera position with better angle for the infinity shape
camera.position.set(0, 3, 10);
camera.lookAt(0, 0, 0);
// Orbit controls with smooth damping
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 5;
controls.maxDistance = 20;
controls.maxPolarAngle = Math.PI * 0.9;
controls.minPolarAngle = Math.PI * 0.1;
// Add environment map for better reflections
const cubeTextureLoader = new THREE.CubeTextureLoader();
const envMap = cubeTextureLoader.load([
'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_px.jpg',
'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_nx.jpg',
'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_py.jpg',
'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_ny.jpg',
'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_pz.jpg',
'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_nz.jpg'
]);
scene.background = envMap;
scene.environment = envMap;
material.envMap = envMap;
// UI controls
const rotationSpeedInput = document.getElementById('rotationSpeed');
const wireframeCheckbox = document.getElementById('wireframe');
const pauseBtn = document.getElementById('pauseBtn');
let isPaused = false;
let autoRotateSpeed = 0.005;
wireframeCheckbox.addEventListener('change', function() {
wireframe.visible = this.checked;
});
pauseBtn.addEventListener('click', function() {
isPaused = !isPaused;
pauseBtn.textContent = isPaused ? 'Play' : 'Pause';
pauseBtn.classList.toggle('bg-yellow-700');
pauseBtn.classList.toggle('bg-yellow-600');
});
// Animation loop with smoother rotation
let time = 0;
function animate() {
requestAnimationFrame(animate);
time += 0.01;
if (!isPaused) {
// Auto rotation with slight variation for visual interest
mobiusStrip.rotation.x += autoRotateSpeed * 0.8;
mobiusStrip.rotation.y += autoRotateSpeed * 0.5;
mobiusStrip.rotation.z += autoRotateSpeed * 0.2;
// Add subtle breathing effect to the light
pointLight.intensity = 2 + Math.sin(time) * 0.5;
pointLight2.intensity = 1.5 + Math.cos(time * 0.8) * 0.3;
// Update rotation speed from UI
autoRotateSpeed = parseFloat(rotationSpeedInput.value);
wireframe.rotation.copy(mobiusStrip.rotation);
}
controls.update();
renderer.render(scene, camera);
}
// Handle window resize
window.addEventListener('resize', function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=etnom/mobius" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>