akhaliq HF Staff commited on
Commit
79619b4
·
verified ·
1 Parent(s): 7bc1bae

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +165 -341
index.html CHANGED
@@ -1,405 +1,229 @@
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
- <title>Nexus Havoc</title>
5
  <style>
6
- body { margin: 0; overflow: hidden; }
7
  #hud {
8
  position: fixed;
9
- top: 20px;
10
- left: 20px;
11
- color: #00ff88;
12
- font-family: 'Orbitron', sans-serif;
13
- font-size: 18px;
14
- text-shadow: 0 0 10px #00ff88;
 
 
 
15
  }
16
- #gameOver {
17
- display: none;
18
  position: fixed;
19
- top: 50%;
20
- left: 50%;
21
- transform: translate(-50%, -50%);
22
- color: #ff0066;
23
- font-size: 3em;
24
- text-align: center;
25
- text-shadow: 0 0 20px #ff0066;
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
  </style>
28
- <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@500&display=swap" rel="stylesheet">
29
  </head>
30
  <body>
31
  <div id="hud">
32
- <div>SPEED: <span id="speed">0</span></div>
33
- <div>SHIELD: <span id="shield">■</span></div>
34
- <div>SCORE: <span id="score">0</span></div>
35
- <div>COMBO: <span id="combo">x1</span></div>
36
  </div>
37
- <div id="gameOver">
38
- <div>SYSTEM FAILURE</div>
39
- <div style="font-size: 0.5em">CLICK TO REBOOT</div>
40
  </div>
41
 
42
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
43
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script>
44
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script>
45
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script>
46
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/GlitchPass.js"></script>
47
-
48
  <script>
49
- let scene, camera, renderer, composer, ship;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  let asteroids = [];
51
  let powerUps = [];
52
- let moveVector = new THREE.Vector3();
53
  let currentSpeed = 0;
54
  let shields = 100;
55
  let score = 0;
56
- let combo = 1;
57
- let lastDodgeTime = 0;
58
- let gameActive = true;
59
- let bossActive = false;
60
-
61
- const config = {
62
- MAX_SPEED: 1.2,
63
- ACCELERATION: 0.0015,
64
- ROT_SPEED: 0.04,
65
- ASTEROID_SPAWN_RATE: 800,
66
- BLOOM_PARAMS: { strength: 0.6, radius: 0.8, threshold: 0.4 }
67
- };
68
-
69
- class ParticleTrail {
70
- constructor() {
71
- this.particles = [];
72
- this.geometry = new THREE.BufferGeometry();
73
- this.material = new THREE.PointsMaterial({
74
- size: 0.3,
75
- color: 0x00ff88,
76
- transparent: true,
77
- blending: THREE.AdditiveBlending
78
- });
79
-
80
- const positions = [];
81
- for(let i = 0; i < 50; i++) {
82
- positions.push(0, 0, 0);
83
- }
84
- this.geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
85
- this.points = new THREE.Points(this.geometry, this.material);
86
- scene.add(this.points);
87
- }
88
-
89
- update(shipPos) {
90
- const positions = this.geometry.attributes.position.array;
91
- for(let i = positions.length - 3; i >= 3; i -= 3) {
92
- positions[i] = positions[i - 3];
93
- positions[i + 1] = positions[i - 2];
94
- positions[i + 2] = positions[i - 1];
95
- }
96
- positions[0] = shipPos.x;
97
- positions[1] = shipPos.y;
98
- positions[2] = shipPos.z - 1;
99
- this.geometry.attributes.position.needsUpdate = true;
100
- }
101
- }
102
-
103
- function init() {
104
- scene = new THREE.Scene();
105
- camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
106
- renderer = new THREE.WebGLRenderer({ antialias: true });
107
- renderer.setSize(window.innerWidth, window.innerHeight);
108
- renderer.setClearColor(0x000000);
109
- document.body.appendChild(renderer.domElement);
110
-
111
- // Post-processing
112
- composer = new THREE.EffectComposer(renderer);
113
- composer.addPass(new THREE.RenderPass(scene, camera));
114
-
115
- const bloomPass = new THREE.UnrealBloomPass(
116
- new THREE.Vector2(window.innerWidth, window.innerHeight),
117
- config.BLOOM_PARAMS.strength,
118
- config.BLOOM_PARAMS.radius,
119
- config.BLOOM_PARAMS.threshold
120
- );
121
- composer.addPass(bloomPass);
122
-
123
- const glitchPass = new THREE.GlitchPass();
124
- glitchPass.goWild = false;
125
- composer.addPass(glitchPass);
126
 
127
- // Lighting
128
- const ambientLight = new THREE.AmbientLight(0x003300);
129
- scene.add(ambientLight);
130
-
131
- const directionalLight = new THREE.DirectionalLight(0x00ff88, 0.8);
132
- directionalLight.position.set(0, 10, 10);
133
- scene.add(directionalLight);
134
 
135
- // Ship
136
- const geometry = new THREE.ConeGeometry(0.6, 2, 8);
137
- const material = new THREE.MeshPhongMaterial({
138
- color: 0x00ff88,
139
- emissive: 0x005500,
140
- shininess: 100,
141
- normalScale: new THREE.Vector2(0.5, 0.5)
142
- });
143
- ship = new THREE.Mesh(geometry, material);
144
- ship.rotation.x = Math.PI / 2;
145
- scene.add(ship);
146
 
147
- // Engine Trail
148
- const trail = new ParticleTrail();
 
 
 
 
 
 
 
149
 
150
- // Starfield
151
- const starsGeometry = new THREE.BufferGeometry();
152
- const starPositions = [];
153
- for(let i = 0; i < 10000; i++) {
154
- starPositions.push(
155
- (Math.random() - 0.5) * 2000,
156
- (Math.random() - 0.5) * 2000,
157
- (Math.random() - 0.5) * 2000
158
- );
159
- }
160
- starsGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starPositions, 3));
161
- const starsMaterial = new THREE.PointsMaterial({
162
- size: 0.1,
163
- color: 0xFFFFFF,
164
- transparent: true,
165
- opacity: 0.8
166
  });
167
- const stars = new THREE.Points(starsGeometry, starsMaterial);
168
- scene.add(stars);
169
-
170
- camera.position.z = 5;
171
- camera.position.y = 2;
172
- }
173
-
174
- function createAsteroid() {
175
- const types = ['normal', 'ice', 'explosive'];
176
- const type = types[Math.floor(Math.random() * types.length)];
177
- const geometry = new THREE.IcosahedronGeometry(Math.random() * 1.2 + 0.8);
178
-
179
- const materials = {
180
- normal: new THREE.MeshPhongMaterial({
181
- color: 0x606060,
182
- emissive: 0x202020,
183
- shininess: 50
184
- }),
185
- ice: new THREE.MeshPhongMaterial({
186
- color: 0x00ffff,
187
- emissive: 0x004444,
188
- transparent: true,
189
- opacity: 0.8
190
- }),
191
- explosive: new THREE.MeshPhongMaterial({
192
- color: 0xff3300,
193
- emissive: 0x551100
194
- })
195
- };
196
-
197
- const asteroid = new THREE.Mesh(geometry, materials[type]);
198
  asteroid.position.set(
199
- Math.random() * 40 - 20,
200
- Math.random() * 40 - 20,
201
- -100
202
  );
203
-
204
- asteroid.velocity = new THREE.Vector3(
205
- 0,
206
- 0,
207
- Math.random() * 0.8 + currentSpeed
208
- );
209
-
210
- asteroid.userData = { type };
211
- scene.add(asteroid);
212
- asteroids.push(asteroid);
213
- }
214
 
215
- function createPowerUp() {
216
- const types = ['shield', 'speed', 'weapon'];
217
- const type = types[Math.floor(Math.random() * types.length)];
218
-
219
- const geometry = new THREE.TorusKnotGeometry(0.4, 0.1, 64, 16);
220
- const material = new THREE.MeshPhongMaterial({
221
- color: type === 'shield' ? 0x0000ff : type === 'speed' ? 0x00ff00 : 0xff00ff,
222
- emissive: type === 'shield' ? 0x000044 : type === 'speed' ? 0x004400 : 0x440044
223
  });
224
-
225
  const powerUp = new THREE.Mesh(geometry, material);
226
  powerUp.position.set(
227
- Math.random() * 40 - 20,
228
- Math.random() * 40 - 20,
229
- -100
230
  );
231
- powerUp.velocity = new THREE.Vector3(0, 0, currentSpeed);
232
- powerUp.userData = { type };
233
- scene.add(powerUp);
234
- powerUps.push(powerUp);
235
- }
236
-
237
- function spawnBoss() {
238
- bossActive = true;
239
- // Boss implementation would go here
240
- }
241
-
242
- function checkCollision(obj) {
243
- const distance = ship.position.distanceTo(obj.position);
244
- return distance < (obj.userData.type === 'ice' ? 2 : 1.8);
245
- }
246
 
247
  function updateHUD() {
248
- document.getElementById('speed').textContent = Math.floor(currentSpeed * 800);
249
- document.getElementById('shield').textContent = '■'.repeat(Math.ceil(shields/20));
250
  document.getElementById('score').textContent = score;
251
- document.getElementById('combo').textContent = `x${combo}`;
252
- }
253
-
254
- function gameOver() {
255
- gameActive = false;
256
- composer.passes[2].goWild = true;
257
- document.getElementById('gameOver').style.display = 'block';
258
- }
259
-
260
- function resetGame() {
261
- asteroids.forEach(a => scene.remove(a));
262
- powerUps.forEach(p => scene.remove(p));
263
- asteroids = [];
264
- powerUps = [];
265
- ship.position.set(0, 0, 0);
266
- currentSpeed = 0;
267
- shields = 100;
268
- score = 0;
269
- combo = 1;
270
- gameActive = true;
271
- bossActive = false;
272
- composer.passes[2].goWild = false;
273
- document.getElementById('gameOver').style.display = 'none';
274
  }
275
 
276
- document.addEventListener('keydown', e => {
277
- if(!gameActive) return;
278
-
279
- switch(e.key) {
280
- case 'ArrowUp': moveVector.y = 1; break;
281
- case 'ArrowDown': moveVector.y = -1; break;
282
- case 'ArrowLeft': moveVector.x = -1; break;
283
- case 'ArrowRight': moveVector.x = 1; break;
284
- }
285
- });
286
-
287
- document.addEventListener('keyup', e => {
288
- switch(e.key) {
289
- case 'ArrowUp':
290
- case 'ArrowDown': moveVector.y = 0; break;
291
- case 'ArrowLeft':
292
- case 'ArrowRight': moveVector.x = 0; break;
293
- }
294
- });
295
-
296
- document.addEventListener('mousemove', e => {
297
- if(gameActive) {
298
- const rect = renderer.domElement.getBoundingClientRect();
299
- moveVector.x = (e.clientX - rect.left) / rect.width * 2 - 1;
300
- moveVector.y = -((e.clientY - rect.top) / rect.height * 2 - 1);
301
- }
302
- });
303
-
304
- document.addEventListener('click', resetGame);
305
-
306
  function animate() {
307
  requestAnimationFrame(animate);
308
 
309
- if(gameActive) {
310
- // Ship movement with inertia
311
- ship.position.x += (moveVector.x * 8 - ship.position.x) * 0.05;
312
- ship.position.y += (moveVector.y * 4 - ship.position.y) * 0.05;
313
-
314
- // Progressive difficulty
315
- currentSpeed = Math.min(config.MAX_SPEED, currentSpeed + config.ACCELERATION);
316
-
317
- // Combo system
318
- if(Date.now() - lastDodgeTime > 3000) combo = 1;
319
 
320
- // Asteroid handling
321
- asteroids.forEach((asteroid, index) => {
322
- asteroid.position.z += asteroid.velocity.z;
323
- asteroid.rotation.x += 0.015;
324
- asteroid.rotation.y += 0.015;
325
 
326
- if(asteroid.position.z > 20) {
327
- scene.remove(asteroid);
328
- asteroids.splice(index, 1);
329
- score += 10 * combo;
330
- lastDodgeTime = Date.now();
331
- }
332
 
333
- if(checkCollision(asteroid)) {
334
- if(asteroid.userData.type === 'ice') {
335
- currentSpeed *= 0.5;
336
- } else if(asteroid.userData.type === 'explosive') {
337
- shields -= 30;
338
- } else {
339
- shields -= 15;
340
- }
341
-
342
- scene.remove(asteroid);
343
- asteroids.splice(index, 1);
344
-
345
- if(shields <= 0) gameOver();
346
- }
347
- });
348
-
349
- // PowerUp handling
350
- powerUps.forEach((powerUp, index) => {
351
- powerUp.position.z += currentSpeed;
352
- powerUp.rotation.x += 0.04;
353
- powerUp.rotation.y += 0.04;
354
-
355
- if(checkCollision(powerUp)) {
356
- switch(powerUp.userData.type) {
357
- case 'shield':
358
- shields = Math.min(100, shields + 40);
359
- break;
360
- case 'speed':
361
- currentSpeed = Math.min(config.MAX_SPEED + 0.3, currentSpeed + 0.2);
362
- break;
363
- }
364
- scene.remove(powerUp);
365
- powerUps.splice(index, 1);
366
- }
367
- });
368
 
369
- // Boss encounter
370
- if(!bossActive && score > 5000) spawnBoss();
 
 
 
 
 
 
 
 
371
 
372
- // Dynamic camera
373
- const cameraOffset = new THREE.Vector3(
374
- ship.position.x * 0.5,
375
- ship.position.y * 0.3 + 3,
376
- 5 + currentSpeed * 2
377
- );
378
- camera.position.lerp(cameraOffset, 0.1);
379
- camera.lookAt(ship.position);
 
 
 
380
  }
381
 
382
- // Post-processing
383
- composer.passes[1].strength = currentSpeed * 0.3;
384
- composer.render();
 
 
 
 
385
 
 
386
  updateHUD();
387
  }
388
 
389
- // Game events
390
- setInterval(() => gameActive && createAsteroid(), config.ASTEROID_SPAWN_RATE);
391
- setInterval(() => gameActive && createPowerUp(), 2500);
392
 
393
- // Initialization
394
  window.addEventListener('resize', () => {
395
  camera.aspect = window.innerWidth / window.innerHeight;
396
  camera.updateProjectionMatrix();
397
  renderer.setSize(window.innerWidth, window.innerHeight);
398
- composer.setSize(window.innerWidth, window.innerHeight);
399
  });
400
 
401
- init();
402
- animate();
 
403
  </script>
404
  </body>
405
  </html>
 
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
+ <title>Space Runner Mobile</title>
5
  <style>
6
+ body { margin: 0; overflow: hidden; touch-action: none; }
7
  #hud {
8
  position: fixed;
9
+ top: 10px;
10
+ left: 10px;
11
+ color: #0f0;
12
+ font-family: Arial;
13
+ font-size: 20px;
14
+ padding: 10px;
15
+ background: rgba(0,0,0,0.7);
16
+ border-radius: 5px;
17
+ z-index: 100;
18
  }
19
+ #controls {
 
20
  position: fixed;
21
+ bottom: 20px;
22
+ width: 100%;
23
+ display: flex;
24
+ justify-content: space-between;
25
+ padding: 0 20px;
26
+ box-sizing: border-box;
27
+ }
28
+ .control-btn {
29
+ width: 60px;
30
+ height: 60px;
31
+ background: rgba(255,255,255,0.2);
32
+ border-radius: 50%;
33
+ display: flex;
34
+ justify-content: center;
35
+ align-items: center;
36
+ color: white;
37
+ font-size: 24px;
38
+ touch-action: none;
39
  }
40
  </style>
 
41
  </head>
42
  <body>
43
  <div id="hud">
44
+ <div>🚀: <span id="speed">0</span></div>
45
+ <div>🛡️: <span id="shield">100</span>%</div>
46
+ <div>⭐: <span id="score">0</span></div>
 
47
  </div>
48
+ <div id="controls">
49
+ <div class="control-btn" id="left">←</div>
50
+ <div class="control-btn" id="right">→</div>
51
  </div>
52
 
53
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
 
 
 
 
 
54
  <script>
55
+ // Scene setup
56
+ const scene = new THREE.Scene();
57
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
58
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
59
+ renderer.setSize(window.innerWidth, window.innerHeight);
60
+ document.body.appendChild(renderer.domElement);
61
+
62
+ // Player Ship (clearly visible pyramid)
63
+ const shipGeometry = new THREE.ConeGeometry(0.8, 2, 4);
64
+ const shipMaterial = new THREE.MeshPhongMaterial({
65
+ color: 0x00ff88,
66
+ emissive: 0x005500
67
+ });
68
+ const ship = new THREE.Mesh(shipGeometry, shipMaterial);
69
+ ship.rotation.x = Math.PI / 2;
70
+ scene.add(ship);
71
+
72
+ // Floor (colorful grid for better orientation)
73
+ const grid = new THREE.GridHelper(40, 40, 0x4444ff, 0x222266);
74
+ scene.add(grid);
75
+
76
+ // Lighting
77
+ const ambientLight = new THREE.AmbientLight(0x404040);
78
+ scene.add(ambientLight);
79
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
80
+ directionalLight.position.set(0, 10, 5);
81
+ scene.add(directionalLight);
82
+
83
+ // Game state
84
  let asteroids = [];
85
  let powerUps = [];
 
86
  let currentSpeed = 0;
87
  let shields = 100;
88
  let score = 0;
89
+ let touchX = 0;
90
+ const MAX_SPEED = 0.3;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ // Mobile controls
93
+ let controls = {
94
+ left: false,
95
+ right: false
96
+ };
 
 
97
 
98
+ // Touch handling
99
+ document.getElementById('left').addEventListener('touchstart', () => controls.left = true);
100
+ document.getElementById('left').addEventListener('touchend', () => controls.left = false);
101
+ document.getElementById('right').addEventListener('touchstart', () => controls.right = true);
102
+ document.getElementById('right').addEventListener('touchend', () => controls.right = false);
 
 
 
 
 
 
103
 
104
+ // Keyboard controls
105
+ window.addEventListener('keydown', (e) => {
106
+ if(e.key === 'ArrowLeft') controls.left = true;
107
+ if(e.key === 'ArrowRight') controls.right = true;
108
+ });
109
+ window.addEventListener('keyup', (e) => {
110
+ if(e.key === 'ArrowLeft') controls.left = false;
111
+ if(e.key === 'ArrowRight') controls.right = false;
112
+ });
113
 
114
+ // Create clearly visible asteroids
115
+ const createAsteroid = () => {
116
+ const size = 1 + Math.random();
117
+ const geometry = new THREE.OctahedronGeometry(size);
118
+ const material = new THREE.MeshPhongMaterial({
119
+ color: 0xff4444,
120
+ emissive: 0x440000
 
 
 
 
 
 
 
 
 
121
  });
122
+ const asteroid = new THREE.Mesh(geometry, material);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  asteroid.position.set(
124
+ (Math.random() - 0.5) * 20,
125
+ (Math.random() - 0.5) * 10,
126
+ -50
127
  );
128
+ return asteroid;
129
+ };
 
 
 
 
 
 
 
 
 
130
 
131
+ // Create power-ups (clearly distinct from asteroids)
132
+ const createPowerUp = () => {
133
+ const geometry = new THREE.TorusGeometry(0.5, 0.2, 8, 24);
134
+ const material = new THREE.MeshPhongMaterial({
135
+ color: 0x00ffff,
136
+ emissive: 0x004444
 
 
137
  });
 
138
  const powerUp = new THREE.Mesh(geometry, material);
139
  powerUp.position.set(
140
+ (Math.random() - 0.5) * 20,
141
+ (Math.random() - 0.5) * 10,
142
+ -50
143
  );
144
+ return powerUp;
145
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
  function updateHUD() {
148
+ document.getElementById('speed').textContent = Math.floor(currentSpeed * 1000);
149
+ document.getElementById('shield').textContent = Math.floor(shields);
150
  document.getElementById('score').textContent = score;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  function animate() {
154
  requestAnimationFrame(animate);
155
 
156
+ // Ship movement controls
157
+ if(controls.left) ship.position.x -= 0.2;
158
+ if(controls.right) ship.position.x += 0.2;
159
+ ship.position.x = THREE.MathUtils.clamp(ship.position.x, -8, 8);
 
 
 
 
 
 
160
 
161
+ // Game progression
162
+ currentSpeed = Math.min(currentSpeed + 0.0001, MAX_SPEED);
163
+ score += Math.floor(currentSpeed * 100);
 
 
164
 
165
+ // Asteroid handling
166
+ asteroids.forEach((asteroid, index) => {
167
+ asteroid.position.z += currentSpeed * 50;
168
+ asteroid.rotation.x += 0.02;
169
+ asteroid.rotation.y += 0.02;
 
170
 
171
+ if(asteroid.position.z > 20) {
172
+ scene.remove(asteroid);
173
+ asteroids.splice(index, 1);
174
+ }
175
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
+ // Power-up handling
178
+ powerUps.forEach((powerUp, index) => {
179
+ powerUp.position.z += currentSpeed * 50;
180
+ powerUp.rotation.x += 0.05;
181
+
182
+ if(powerUp.position.z > 20) {
183
+ scene.remove(powerUp);
184
+ powerUps.splice(index, 1);
185
+ }
186
+ });
187
 
188
+ // Spawning (slower pace for mobile)
189
+ if(Math.random() < 0.015) {
190
+ const asteroid = createAsteroid();
191
+ scene.add(asteroid);
192
+ asteroids.push(asteroid);
193
+ }
194
+
195
+ if(Math.random() < 0.008) {
196
+ const powerUp = createPowerUp();
197
+ scene.add(powerUp);
198
+ powerUps.push(powerUp);
199
  }
200
 
201
+ // Camera follow
202
+ camera.position.set(
203
+ ship.position.x * 0.5,
204
+ ship.position.y + 3,
205
+ 5 + currentSpeed * 50
206
+ );
207
+ camera.lookAt(ship.position);
208
 
209
+ renderer.render(scene, camera);
210
  updateHUD();
211
  }
212
 
213
+ // Initial setup
214
+ camera.position.z = 5;
215
+ animate();
216
 
217
+ // Responsive handling
218
  window.addEventListener('resize', () => {
219
  camera.aspect = window.innerWidth / window.innerHeight;
220
  camera.updateProjectionMatrix();
221
  renderer.setSize(window.innerWidth, window.innerHeight);
 
222
  });
223
 
224
+ // Prevent touch scrolling
225
+ document.body.addEventListener('touchstart', (e) => e.preventDefault(), { passive: false });
226
+ document.body.addEventListener('touchmove', (e) => e.preventDefault(), { passive: false });
227
  </script>
228
  </body>
229
  </html>