Download index.html from etnom/mobius: direct link, hf CLI and curl.
- Browser
- Download file 12.9 kB
-
https://huggingface.co/spaces/etnom/mobius/resolve/main/index.html
- Command line
-
hf download hf://spaces/etnom/mobius/index.html
-
curl -L -o index.html https://huggingface.co/spaces/etnom/mobius/resolve/main/index.html
12.9 kB
| <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> |