Spaces:
Running
Running
import gradio as gr | |
demo = gr.Blocks() | |
with demo: | |
gr.HTML("""<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Hugging Face Game with Sound</title> | |
<style> | |
body { margin: 0; overflow: hidden; } | |
canvas { display: block; } | |
#win-message { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 48px; | |
color: white; | |
background: rgba(0, 0, 0, 0.7); | |
padding: 20px; | |
display: none; | |
font-family: Arial, sans-serif; | |
} | |
#gpu-counter { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
font-size: 24px; | |
color: white; | |
background: rgba(0, 0, 0, 0.7); | |
padding: 10px; | |
font-family: Arial, sans-serif; | |
} | |
#controls { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 20px; | |
color: white; | |
background: rgba(0, 0, 0, 0.8); | |
padding: 20px; | |
font-family: Arial, sans-serif; | |
text-align: center; | |
transition: opacity 1s; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="win-message">You Win!</div> | |
<div id="gpu-counter">GPUs Collected: 0 / 10</div> | |
<div id="controls"> | |
<p>Controls:</p> | |
<p>W / Up Arrow: Forward</p> | |
<p>S / Down Arrow: Backward</p> | |
<p>A / Left Arrow: Left</p> | |
<p>D / Right Arrow: Right</p> | |
<p>Spacebar: Up</p> | |
<p>Shift: Down</p> | |
<p>Collect all GPUs to win!</p> | |
</div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script> | |
<script> | |
// Scene setup | |
const scene = new THREE.Scene(); | |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
const renderer = new THREE.WebGLRenderer(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
document.body.appendChild(renderer.domElement); | |
// Morning lighting | |
const directionalLight = new THREE.DirectionalLight(0xffe0b2, 1); | |
directionalLight.position.set(1, 1, 0.5).normalize(); | |
scene.add(directionalLight); | |
const ambientLight = new THREE.AmbientLight(0x87ceeb, 0.5); | |
scene.add(ambientLight); | |
// Morning background | |
scene.background = new THREE.Color(0x87ceeb); | |
scene.fog = new THREE.Fog(0x87ceeb, 10, 100); | |
// Ground | |
const groundGeometry = new THREE.PlaneGeometry(100, 100); | |
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x8c8c8c }); | |
const ground = new THREE.Mesh(groundGeometry, groundMaterial); | |
ground.rotation.x = -Math.PI / 2; | |
scene.add(ground); | |
// Realistic Buildings | |
const buildings = []; | |
const numBuildings = 50; | |
for (let i = 0; i < numBuildings; i++) { | |
const width = Math.random() * 5 + 2; | |
const height = Math.random() * 20 + 10; | |
const depth = Math.random() * 5 + 2; | |
const buildingGeometry = new THREE.BoxGeometry(width, height, depth); | |
const buildingMaterial = new THREE.MeshLambertMaterial({ color: 0xb0b0b0 }); | |
const building = new THREE.Mesh(buildingGeometry, buildingMaterial); | |
building.position.x = Math.random() * 80 - 40; | |
building.position.y = height / 2; | |
building.position.z = Math.random() * 80 - 40; | |
scene.add(building); | |
buildings.push(building); | |
} | |
// Hugging Face Emoji | |
const emojiCanvas = document.createElement('canvas'); | |
emojiCanvas.width = 256; | |
emojiCanvas.height = 256; | |
const emojiCtx = emojiCanvas.getContext('2d'); | |
emojiCtx.font = '128px Arial'; | |
emojiCtx.textAlign = 'center'; | |
emojiCtx.textBaseline = 'middle'; | |
emojiCtx.fillText('🤗', 128, 128); | |
const emojiTexture = new THREE.CanvasTexture(emojiCanvas); | |
const faceGeometry = new THREE.SphereGeometry(1, 32, 32); | |
const faceMaterial = new THREE.MeshLambertMaterial({ map: emojiTexture }); | |
const face = new THREE.Mesh(faceGeometry, faceMaterial); | |
face.position.set(0, 5, 0); | |
scene.add(face); | |
face.velocity = new THREE.Vector3(0, 0, 0); | |
face.maxSpeed = 25; | |
face.acceleration = new THREE.Vector3(0, 0, 0); | |
face.friction = 0.95; | |
// "GPU" Texts | |
const canvas = document.createElement('canvas'); | |
canvas.width = 128; | |
canvas.height = 128; | |
const ctx = canvas.getContext('2d'); | |
ctx.font = '48px Arial'; | |
ctx.fillStyle = 'white'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
ctx.fillText('GPU', 64, 64); | |
const texture = new THREE.CanvasTexture(canvas); | |
const gpuSprites = []; | |
const totalGPUs = 10; | |
let collectedGPUs = 0; | |
for (let i = 0; i < totalGPUs; i++) { | |
const spriteMaterial = new THREE.SpriteMaterial({ map: texture }); | |
const sprite = new THREE.Sprite(spriteMaterial); | |
sprite.scale.set(2, 2, 1); | |
sprite.position.set(Math.random() * 80 - 40, Math.random() * 20 + 10, Math.random() * 80 - 40); | |
scene.add(sprite); | |
gpuSprites.push(sprite); | |
} | |
// Sound setup (Web Audio API) | |
const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); | |
// Background music (simple looping oscillator) | |
const bgOscillator = audioCtx.createOscillator(); | |
const bgGain = audioCtx.createGain(); | |
bgOscillator.type = 'sine'; | |
bgOscillator.frequency.setValueAtTime(220, audioCtx.currentTime); // Low hum | |
bgGain.gain.setValueAtTime(0.1, audioCtx.currentTime); // Quiet volume | |
bgOscillator.connect(bgGain); | |
bgGain.connect(audioCtx.destination); | |
bgOscillator.start(); | |
// GPU collect sound | |
function playCollectSound() { | |
const oscillator = audioCtx.createOscillator(); | |
const gain = audioCtx.createGain(); | |
oscillator.type = 'square'; | |
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime); // High pitch | |
gain.gain.setValueAtTime(0.3, audioCtx.currentTime); | |
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.2); | |
oscillator.connect(gain); | |
gain.connect(audioCtx.destination); | |
oscillator.start(); | |
oscillator.stop(audioCtx.currentTime + 0.2); | |
} | |
// Clock for smooth animation | |
const clock = new THREE.Clock(); | |
// Win condition and counter | |
const winMessage = document.getElementById('win-message'); | |
const gpuCounter = document.getElementById('gpu-counter'); | |
let gameWon = false; | |
// Controls instructions | |
const controlsDiv = document.getElementById('controls'); | |
let controlsVisible = true; | |
setTimeout(() => { | |
if (controlsVisible) { | |
controlsDiv.style.opacity = '0'; | |
setTimeout(() => controlsDiv.style.display = 'none', 1000); | |
controlsVisible = false; | |
} | |
}, 5000); // Hide after 5 seconds | |
// Keyboard controls | |
const keys = {}; | |
window.addEventListener('keydown', (e) => { | |
keys[e.key] = true; | |
if (controlsVisible) { | |
controlsDiv.style.opacity = '0'; | |
setTimeout(() => controlsDiv.style.display = 'none', 1000); | |
controlsVisible = false; | |
} | |
}); | |
window.addEventListener('keyup', (e) => { keys[e.key] = false; }); | |
function handleControls(deltaTime) { | |
const accel = 50; | |
face.acceleration.set(0, 0, 0); | |
if (keys['w'] || keys['ArrowUp']) face.acceleration.z = -accel; | |
if (keys['s'] || keys['ArrowDown']) face.acceleration.z = accel; | |
if (keys['a'] || keys['ArrowLeft']) face.acceleration.x = -accel; | |
if (keys['d'] || keys['ArrowRight']) face.acceleration.x = accel; | |
if (keys[' ']) face.acceleration.y = accel; | |
if (keys['Shift']) face.acceleration.y = -accel; | |
// Apply acceleration and friction | |
face.velocity.add(face.acceleration.clone().multiplyScalar(deltaTime)); | |
face.velocity.multiplyScalar(face.friction); | |
face.velocity.clampLength(0, face.maxSpeed); | |
// Update position | |
face.position.add(face.velocity.clone().multiplyScalar(deltaTime)); | |
// Face direction of movement | |
if (face.velocity.length() > 0.1) { | |
face.lookAt(face.position.clone().add(face.velocity)); | |
} | |
// Keep within bounds | |
face.position.x = Math.max(-50, Math.min(50, face.position.x)); | |
face.position.z = Math.max(-50, Math.min(50, face.position.z)); | |
face.position.y = Math.max(1, Math.min(50, face.position.y)); | |
} | |
// Animation loop | |
function animate() { | |
requestAnimationFrame(animate); | |
const deltaTime = clock.getDelta(); | |
if (!gameWon) { | |
// Handle player controls | |
handleControls(deltaTime); | |
// Check for "GPU" collisions | |
gpuSprites.forEach((gpu, index) => { | |
if (face.position.distanceTo(gpu.position) < 1) { | |
scene.remove(gpu); | |
gpuSprites.splice(index, 1); | |
collectedGPUs++; | |
gpuCounter.textContent = `GPUs Collected: ${collectedGPUs} / ${totalGPUs}`; | |
playCollectSound(); // Play sound on collection | |
if (collectedGPUs === totalGPUs) { | |
gameWon = true; | |
winMessage.style.display = 'block'; | |
face.velocity.set(0, 0, 0); | |
bgOscillator.stop(); // Stop background music | |
} | |
} | |
}); | |
} | |
// Update camera | |
camera.position.set(face.position.x, face.position.y + 5, face.position.z - 10); | |
camera.lookAt(face.position); | |
// Render | |
renderer.render(scene, camera); | |
} | |
animate(); | |
// Handle window resize | |
window.addEventListener('resize', () => { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
}); | |
</script> | |
</body> | |
</html>""") | |
demo.launch() |