REMNANT
Survival · Open World · v1.0
0%
INITIALIZING ENGINE
REMNANT
06:00 · DAWN
❤
100
🍖
80
💧
75
⚡
100
[ E ] INTERACT
NS EW
BEARING
WASD·MOVE  |  SHIFT·SPRINT  |  SPACE·JUMP  |  I·INV  |  C·CRAFT  |  B·BUILD  |  E·INTERACT  |  F·VIEW  |  LMB·ATTACK  |  M·MAP  |  T·CHAT
SPRINTING
REMNANT v1.0 · OPEN WORLD SURVIVAL
ONLINE · 4 PLAYERS
⚙ SETTINGS
MINIMAP
🔊 AUDIO
↑
⚔
E
⚡
🎒
⚒
SETTINGS
SHADOWS
FOG
SOUND
MUSIC
QUALITY
MASTER VOL
RENDER DIST
[ CLOSE ]
INVENTORY [ ESC ]
CLICK ITEM TO EQUIP / USE  ·  PRESS I OR ESC TO CLOSE
YOU DIED
REMNANT CLAIMED YOU
CRAFTING BENCH[ ESC ]
🟫FLOOR
🧱WALL
🔶ROOF
🚪DOOR
🔥FIRE
LMB
PLACE
B·EXIT
1
2
3
4
5
setLoad(5, 'BUILDING TERRAIN'); await sleep(80); const terrain = buildTerrain(); setLoad(16, 'FILLING WORLD'); await sleep(60); buildWater(); setLoad(24, 'GROWING FOREST'); await sleep(60); buildForest(terrain); setLoad(38, 'PLACING ROCKS'); await sleep(50); buildRocks(terrain); setLoad(46, 'PLANTING GRASS'); await sleep(40); buildGrass(terrain); setLoad(53, 'SCATTERING LOOT'); await sleep(40); buildLootObjects(terrain); setLoad(60, 'PAINTING SKY'); await sleep(40); const skyMat = buildSky(); setLoad(67, 'SUMMONING CLOUDS'); await sleep(40); const clouds = buildClouds(); setLoad(73, 'LIGHTING SCENE'); await sleep(40); const lights = buildLights(); setLoad(79, 'LIGHTING CAMPFIRE'); await sleep(40); const fire = buildCampfire(terrain); setLoad(85, 'CREATING SURVIVOR'); await sleep(40); const playerModel = buildPlayerModel();setLoad(90, 'SPAWNING ENEMIES'); await sleep(40); spawnEnemyWaves(terrain); setLoad(98, 'ENTERING WORLD'); await sleep(300); const spawnH = getTerrainHeight(terrain, 0, 0) + .92; playerY = spawnH; playerPos.set(0, spawnH, 0); playerModel.position.set(0, spawnH - .92, 0); camera.position.set(0, spawnH + 1.5, 4); // Init hotbar updateHotbarDisplay(); setLoad(100, 'WELCOME TO REMNANT'); await sleep(700); loader.classList.add('hidden'); setTimeout(() => { loader.style.display = 'none'; hud.classList.add('visible'); showNotif('REMNANT · WASD MOVE · C CRAFT · B BUILD · LMB ATTACK'); }, 1300); // Night cold damage setInterval(() => { if (!isDead && (gameTime > 21 || gameTime < 4)) { takeDamage(4); showNotif('❄ FREEZING NIGHT · FIND SHELTER OR BUILD A FIRE'); } }, 28000); // Respawn enemies that are all dead setInterval(() => { const alive = enemies.filter(e => !e.dead).length; if (alive < 5) { const typeDef = ENEMY_TYPES[Math.floor(Math.random()*ENEMY_TYPES.length)]; const angle = Math.random()*Math.PI*2, r = 50+Math.random()*140; spawnEnemy(terrain, typeDef, Math.cos(angle)*r, Math.sin(angle)*r); } }, 15000); let fireFlicker = 0; function animate() { requestAnimationFrame(animate); const dt = Math.min(clock.getDelta(), .05); const elapsed = clock.getElapsedTime(); if (!isDead && !inventoryOpen && !craftingOpen) updatePlayer(terrain, playerModel, dt); updateStats(dt); updateDayNight(lights, skyMat, dt); updateEnemies(terrain, dt); if (buildMode) updateBuildGhost(terrain); // Fire flicker fireFlicker += dt * 12; fire.fireLight.intensity = 3 + Math.sin(fireFlicker)*.8 + Math.sin(fireFlicker*2.3)*.4; fire.fireMesh.scale.setScalar(.9+Math.sin(fireFlicker*1.7)*.15); fire.embers.forEach(em => { em.userData.life += dt * .8; if (em.userData.life > 1) { em.userData.life=0; em.position.set((Math.random()-.5)*.3,.4+Math.random()*.2,(Math.random()-.5)*.3); } em.position.addScaledVector(em.userData.vel, dt*em.userData.life*.3); em.position.y += dt*.8; em.material.opacity = 1-em.userData.life; em.material.transparent = true; }); // Cloud drift clouds.forEach((c,i) => { c.position.x += dt*(.8+i*.04); if(c.position.x>300) c.position.x=-300; }); skyMat.uniforms.uTime.value = elapsed; renderer.render(scene, camera); } animate(); } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } boot().catch(console.error); // ───────────────────────────────── // PHASE 5: PWA + PERFORMANCE // ───────────────────────────────── if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') .then(r => console.log('[REMNANT] SW:', r.scope)) .catch(e => console.warn('[REMNANT] SW fail:', e)); }); } (function() { const el = document.createElement('div'); el.style.cssText = 'position:fixed;top:22px;left:90px;font-family:monospace;font-size:8px;letter-spacing:.2em;color:rgba(200,184,154,.2);z-index:20;pointer-events:none'; document.body.appendChild(el); let f=0,last=performance.now(); function loop(){ f++; const n=performance.now(); if(n-last>=1000){ const fps=f; el.textContent=fps+' FPS'; el.style.color=fps>=50?'rgba(100,200,100,.25)':fps>=30?'rgba(200,180,80,.3)':'rgba(220,80,60,.4)'; f=0; last=n; } requestAnimationFrame(loop); } loop(); })(); (function() { const btn = document.createElement('div'); btn.textContent = '← MENU'; btn.style.cssText = 'position:fixed;top:22px;left:28px;font-family:monospace;font-size:8px;letter-spacing:.25em;color:rgba(200,184,154,.2);z-index:20;cursor:pointer;transition:color .2s ease'; btn.onmouseover=()=>btn.style.color='rgba(200,184,154,.7)'; btn.onmouseout=()=>btn.style.color='rgba(200,184,154,.2)'; btn.onclick=()=>{ if(confirm('Return to main menu?')) window.location.href='/'; }; document.body.appendChild(btn); })(); document.getElementById('gameCanvas').addEventListener('contextmenu', e => e.preventDefault()); document.body.addEventListener('touchmove', e => e.preventDefault(), { passive: false }); (async()=>{ try{ if('wakeLock' in navigator) await navigator.wakeLock.request('screen'); }catch(e){} })(); document.addEventListener('visibilitychange', async()=>{ try{ if(document.visibilityState==='visible'&&'wakeLock' in navigator) await navigator.wakeLock.request('screen'); }catch(e){} });