awacke1 commited on
Commit
2044d3c
·
verified ·
1 Parent(s): 27a05e4

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +542 -19
index.html CHANGED
@@ -1,19 +1,542 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Lake Minnetonka - Mound Docks at Dusk</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
8
+ <style>
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ background: #1a1a2e;
13
+ overflow: hidden;
14
+ font-family: 'Georgia', serif;
15
+ }
16
+
17
+ canvas {
18
+ display: block;
19
+ }
20
+
21
+ .controls {
22
+ position: absolute;
23
+ top: 20px;
24
+ left: 20px;
25
+ color: #d4af37;
26
+ background: rgba(0, 0, 0, 0.7);
27
+ padding: 15px;
28
+ border-radius: 10px;
29
+ border: 1px solid #8B4513;
30
+ z-index: 100;
31
+ }
32
+
33
+ .controls h3 {
34
+ margin: 0 0 10px 0;
35
+ color: #daa520;
36
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
37
+ }
38
+
39
+ .controls p {
40
+ margin: 5px 0;
41
+ font-size: 14px;
42
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
43
+ }
44
+
45
+ .title {
46
+ position: absolute;
47
+ bottom: 30px;
48
+ left: 50%;
49
+ transform: translateX(-50%);
50
+ color: #d4af37;
51
+ text-align: center;
52
+ z-index: 100;
53
+ background: rgba(0, 0, 0, 0.8);
54
+ padding: 15px 25px;
55
+ border-radius: 15px;
56
+ border: 2px solid #8B4513;
57
+ }
58
+
59
+ .title h1 {
60
+ margin: 0;
61
+ font-size: 24px;
62
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
63
+ color: #daa520;
64
+ }
65
+
66
+ .title p {
67
+ margin: 5px 0 0 0;
68
+ font-style: italic;
69
+ font-size: 16px;
70
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
71
+ }
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <div class="controls">
76
+ <h3>Lake Minnetonka Experience</h3>
77
+ <p>🎮 Mouse: Look around</p>
78
+ <p>⬅️➡️ Arrow keys: Move left/right</p>
79
+ <p>⬆️⬇️ Arrow keys: Move forward/back</p>
80
+ <p>🔧 WASD: Alternative movement</p>
81
+ <p>✨ Watch the fireflies rise at dusk</p>
82
+ </div>
83
+
84
+ <div class="title">
85
+ <h1>Mound Docks at Dusk</h1>
86
+ <p>"These Mound docks at dusk are almost more than I can bear..."</p>
87
+ </div>
88
+
89
+ <script>
90
+ // Scene setup
91
+ const scene = new THREE.Scene();
92
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
93
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
94
+ renderer.setSize(window.innerWidth, window.innerHeight);
95
+ renderer.shadowMap.enabled = true;
96
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
97
+ renderer.fog = true;
98
+ document.body.appendChild(renderer.domElement);
99
+
100
+ // Atmospheric fog
101
+ scene.fog = new THREE.Fog(0x2d1810, 50, 800);
102
+
103
+ // Camera controls
104
+ let mouseX = 0, mouseY = 0;
105
+ let cameraRotationX = 0, cameraRotationY = 0;
106
+ const keys = {};
107
+
108
+ // Position camera on the dock
109
+ camera.position.set(0, 3, 15);
110
+ camera.lookAt(0, 0, 0);
111
+
112
+ // Create bruising dusk sky
113
+ const skyGeometry = new THREE.SphereGeometry(1000, 32, 32);
114
+ const skyMaterial = new THREE.ShaderMaterial({
115
+ uniforms: {
116
+ time: { value: 0 }
117
+ },
118
+ vertexShader: `
119
+ varying vec3 vWorldPosition;
120
+ void main() {
121
+ vec4 worldPosition = modelMatrix * vec4(position, 1.0);
122
+ vWorldPosition = worldPosition.xyz;
123
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
124
+ }
125
+ `,
126
+ fragmentShader: `
127
+ uniform float time;
128
+ varying vec3 vWorldPosition;
129
+ void main() {
130
+ vec3 direction = normalize(vWorldPosition);
131
+ float elevation = direction.y;
132
+
133
+ // Bruising sky colors
134
+ vec3 darkPurple = vec3(0.1, 0.05, 0.2);
135
+ vec3 deepBlue = vec3(0.05, 0.1, 0.3);
136
+ vec3 rustGold = vec3(0.8, 0.4, 0.1);
137
+ vec3 bloodRed = vec3(0.6, 0.1, 0.1);
138
+
139
+ // Sun bleeding effect on horizon
140
+ float horizonGlow = exp(-abs(elevation) * 2.0);
141
+ float sunBleed = sin(direction.x * 2.0 + time * 0.5) * 0.3 + 0.7;
142
+
143
+ vec3 color = mix(darkPurple, deepBlue, elevation + 0.5);
144
+ color = mix(color, rustGold, horizonGlow * sunBleed * 0.8);
145
+ color = mix(color, bloodRed, horizonGlow * 0.3);
146
+
147
+ gl_FragColor = vec4(color, 1.0);
148
+ }
149
+ `,
150
+ side: THREE.BackSide
151
+ });
152
+ const sky = new THREE.Mesh(skyGeometry, skyMaterial);
153
+ scene.add(sky);
154
+
155
+ // Create water surface with bleeding sun reflection
156
+ const waterGeometry = new THREE.PlaneGeometry(1000, 1000, 128, 128);
157
+ const waterMaterial = new THREE.ShaderMaterial({
158
+ uniforms: {
159
+ time: { value: 0 },
160
+ sunPosition: { value: new THREE.Vector3(100, 10, -200) }
161
+ },
162
+ vertexShader: `
163
+ uniform float time;
164
+ varying vec2 vUv;
165
+ varying vec3 vPosition;
166
+
167
+ void main() {
168
+ vUv = uv;
169
+
170
+ // Wave animation
171
+ vec3 pos = position;
172
+ float wave1 = sin(pos.x * 0.02 + time) * 0.3;
173
+ float wave2 = sin(pos.y * 0.015 + time * 1.3) * 0.2;
174
+ float wave3 = sin((pos.x + pos.y) * 0.01 + time * 0.8) * 0.4;
175
+ pos.z = wave1 + wave2 + wave3;
176
+
177
+ vPosition = pos;
178
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
179
+ }
180
+ `,
181
+ fragmentShader: `
182
+ uniform float time;
183
+ uniform vec3 sunPosition;
184
+ varying vec2 vUv;
185
+ varying vec3 vPosition;
186
+
187
+ void main() {
188
+ // Base water color
189
+ vec3 deepWater = vec3(0.1, 0.2, 0.3);
190
+ vec3 shallowWater = vec3(0.2, 0.3, 0.4);
191
+
192
+ // Sun bleeding reflection - rust and gold
193
+ vec3 rustColor = vec3(0.8, 0.3, 0.1);
194
+ vec3 goldColor = vec3(1.0, 0.8, 0.2);
195
+
196
+ // Calculate distance from sun reflection point
197
+ vec2 sunReflection = vec2(0.0, -0.3); // Approximate sun position on water
198
+ float distToSun = length(vUv - sunReflection);
199
+
200
+ // Bleeding sun effect
201
+ float sunIntensity = exp(-distToSun * 3.0) * (sin(time) * 0.2 + 0.8);
202
+ vec3 sunBleed = mix(rustColor, goldColor, sin(time * 2.0) * 0.5 + 0.5);
203
+
204
+ // Wave reflections
205
+ float wave = sin(vPosition.x * 0.1 + time) * sin(vPosition.y * 0.08 + time * 1.2);
206
+ vec3 waterColor = mix(deepWater, shallowWater, wave * 0.5 + 0.5);
207
+
208
+ // Final color with bleeding sun
209
+ vec3 finalColor = mix(waterColor, sunBleed, sunIntensity);
210
+
211
+ gl_FragColor = vec4(finalColor, 0.9);
212
+ }
213
+ `,
214
+ transparent: true
215
+ });
216
+ waterGeometry.rotateX(-Math.PI / 2);
217
+ const water = new THREE.Mesh(waterGeometry, waterMaterial);
218
+ water.position.y = 0;
219
+ scene.add(water);
220
+
221
+ // Create wooden pier with weathered texture
222
+ function createPier() {
223
+ const pierGroup = new THREE.Group();
224
+
225
+ // Main pier planks
226
+ for (let i = 0; i < 20; i++) {
227
+ const plankGeometry = new THREE.BoxGeometry(2, 0.1, 0.8);
228
+ const plankMaterial = new THREE.MeshLambertMaterial({
229
+ color: new THREE.Color(0.4 + Math.random() * 0.2, 0.25, 0.15)
230
+ });
231
+ const plank = new THREE.Mesh(plankGeometry, plankMaterial);
232
+ plank.position.set(0, 0.5, -i * 0.9);
233
+ plank.castShadow = true;
234
+ pierGroup.add(plank);
235
+ }
236
+
237
+ // Pier supports
238
+ for (let i = 0; i < 5; i++) {
239
+ const supportGeometry = new THREE.CylinderGeometry(0.1, 0.1, 3);
240
+ const supportMaterial = new THREE.MeshLambertMaterial({ color: 0x3d2817 });
241
+ const support = new THREE.Mesh(supportGeometry, supportMaterial);
242
+ support.position.set(-0.8, -0.5, -i * 4);
243
+ pierGroup.add(support);
244
+
245
+ const support2 = support.clone();
246
+ support2.position.x = 0.8;
247
+ pierGroup.add(support2);
248
+ }
249
+
250
+ return pierGroup;
251
+ }
252
+
253
+ const pier = createPier();
254
+ scene.add(pier);
255
+
256
+ // Create pontoon boat "hanging its head"
257
+ function createPontoon() {
258
+ const pontoonGroup = new THREE.Group();
259
+
260
+ // Pontoon floats
261
+ const pontoonGeometry = new THREE.CylinderGeometry(0.3, 0.3, 8);
262
+ const pontoonMaterial = new THREE.MeshLambertMaterial({ color: 0x666666 });
263
+
264
+ const leftPontoon = new THREE.Mesh(pontoonGeometry, pontoonMaterial);
265
+ leftPontoon.rotation.z = Math.PI / 2;
266
+ leftPontoon.position.set(-1.5, 0.3, -25);
267
+ pontoonGroup.add(leftPontoon);
268
+
269
+ const rightPontoon = leftPontoon.clone();
270
+ rightPontoon.position.x = 1.5;
271
+ pontoonGroup.add(rightPontoon);
272
+
273
+ // Deck
274
+ const deckGeometry = new THREE.BoxGeometry(4, 0.1, 8);
275
+ const deckMaterial = new THREE.MeshLambertMaterial({ color: 0x8B7355 });
276
+ const deck = new THREE.Mesh(deckGeometry, deckMaterial);
277
+ deck.position.set(0, 0.8, -25);
278
+ pontoonGroup.add(deck);
279
+
280
+ // Mast for morse code slapping
281
+ const mastGeometry = new THREE.CylinderGeometry(0.05, 0.05, 6);
282
+ const mastMaterial = new THREE.MeshLambertMaterial({ color: 0x654321 });
283
+ const mast = new THREE.Mesh(mastGeometry, mastMaterial);
284
+ mast.position.set(0, 3.8, -25);
285
+ pontoonGroup.add(mast);
286
+
287
+ // Make pontoon "hang its head" - slight rotation
288
+ pontoonGroup.rotation.x = 0.1;
289
+ pontoonGroup.position.y = -0.3; // Lower than it was "back then"
290
+
291
+ return pontoonGroup;
292
+ }
293
+
294
+ const pontoon = createPontoon();
295
+ scene.add(pontoon);
296
+
297
+ // Create plastic chair with sweating beer
298
+ function createChairAndBeer() {
299
+ const chairGroup = new THREE.Group();
300
+
301
+ // Plastic chair
302
+ const seatGeometry = new THREE.BoxGeometry(1.5, 0.1, 1.5);
303
+ const chairMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
304
+ const seat = new THREE.Mesh(seatGeometry, chairMaterial);
305
+ seat.position.set(3, 1, 8);
306
+ chairGroup.add(seat);
307
+
308
+ // Chair back
309
+ const backGeometry = new THREE.BoxGeometry(1.5, 2, 0.1);
310
+ const back = new THREE.Mesh(backGeometry, chairMaterial);
311
+ back.position.set(3, 2, 7.3);
312
+ chairGroup.add(back);
313
+
314
+ // Chair legs
315
+ for (let i = 0; i < 4; i++) {
316
+ const legGeometry = new THREE.CylinderGeometry(0.03, 0.03, 1);
317
+ const leg = new THREE.Mesh(legGeometry, chairMaterial);
318
+ leg.position.set(
319
+ 3 + (i % 2 ? 0.6 : -0.6),
320
+ 0.5,
321
+ 8 + (i < 2 ? 0.6 : -0.6)
322
+ );
323
+ chairGroup.add(leg);
324
+ }
325
+
326
+ // Beer can
327
+ const beerGeometry = new THREE.CylinderGeometry(0.15, 0.15, 0.5);
328
+ const beerMaterial = new THREE.MeshLambertMaterial({ color: 0xDAA520 });
329
+ const beer = new THREE.Mesh(beerGeometry, beerMaterial);
330
+ beer.position.set(3.8, 1.35, 8);
331
+ chairGroup.add(beer);
332
+
333
+ return chairGroup;
334
+ }
335
+
336
+ const chairAndBeer = createChairAndBeer();
337
+ scene.add(chairAndBeer);
338
+
339
+ // Create scattered tackle and rusty magnet
340
+ function createTackle() {
341
+ const tackleGroup = new THREE.Group();
342
+
343
+ // Scattered tackle items
344
+ for (let i = 0; i < 15; i++) {
345
+ const tackleGeometry = new THREE.SphereGeometry(0.1, 8, 8);
346
+ const tackleMaterial = new THREE.MeshLambertMaterial({
347
+ color: new THREE.Color(Math.random(), Math.random(), Math.random())
348
+ });
349
+ const tackle = new THREE.Mesh(tackleGeometry, tackleMaterial);
350
+ tackle.position.set(
351
+ (Math.random() - 0.5) * 10,
352
+ 0.6,
353
+ Math.random() * 5
354
+ );
355
+ tackleGroup.add(tackle);
356
+ }
357
+
358
+ // Rusty magnet on fraying twine
359
+ const magnetGeometry = new THREE.BoxGeometry(0.3, 0.1, 0.3);
360
+ const magnetMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
361
+ const magnet = new THREE.Mesh(magnetGeometry, magnetMaterial);
362
+ magnet.position.set(-2, 0.6, 5);
363
+ tackleGroup.add(magnet);
364
+
365
+ return tackleGroup;
366
+ }
367
+
368
+ const tackle = createTackle();
369
+ scene.add(tackle);
370
+
371
+ // Create mansions on the shore
372
+ function createMansions() {
373
+ const mansionGroup = new THREE.Group();
374
+
375
+ for (let i = 0; i < 5; i++) {
376
+ const mansionGeometry = new THREE.BoxGeometry(
377
+ 8 + Math.random() * 4,
378
+ 6 + Math.random() * 4,
379
+ 12 + Math.random() * 6
380
+ );
381
+ const mansionMaterial = new THREE.MeshLambertMaterial({
382
+ color: new THREE.Color(0.9, 0.9, 0.8)
383
+ });
384
+ const mansion = new THREE.Mesh(mansionGeometry, mansionMaterial);
385
+ mansion.position.set(
386
+ 40 + i * 25 + Math.random() * 10,
387
+ mansion.geometry.parameters.height / 2,
388
+ -100 + Math.random() * 200
389
+ );
390
+ mansion.castShadow = true;
391
+ mansionGroup.add(mansion);
392
+ }
393
+
394
+ return mansionGroup;
395
+ }
396
+
397
+ const mansions = createMansions();
398
+ scene.add(mansions);
399
+
400
+ // Fireflies rising like embers
401
+ const fireflies = [];
402
+ const fireflyGeometry = new THREE.SphereGeometry(0.1, 8, 8);
403
+ const fireflyMaterial = new THREE.MeshBasicMaterial({
404
+ color: 0xFFFF80,
405
+ transparent: true,
406
+ opacity: 0.8
407
+ });
408
+
409
+ for (let i = 0; i < 100; i++) {
410
+ const firefly = new THREE.Mesh(fireflyGeometry, fireflyMaterial);
411
+ firefly.position.set(
412
+ (Math.random() - 0.5) * 100,
413
+ Math.random() * 2,
414
+ (Math.random() - 0.5) * 100
415
+ );
416
+ firefly.userData = {
417
+ speed: Math.random() * 0.02 + 0.01,
418
+ phase: Math.random() * Math.PI * 2
419
+ };
420
+ fireflies.push(firefly);
421
+ scene.add(firefly);
422
+ }
423
+
424
+ // Lighting setup
425
+ const ambientLight = new THREE.AmbientLight(0x404060, 0.3);
426
+ scene.add(ambientLight);
427
+
428
+ // Sunset directional light (bleeding sun)
429
+ const sunLight = new THREE.DirectionalLight(0xFF6B35, 0.8);
430
+ sunLight.position.set(100, 20, -200);
431
+ sunLight.castShadow = true;
432
+ sunLight.shadow.mapSize.width = 2048;
433
+ sunLight.shadow.mapSize.height = 2048;
434
+ scene.add(sunLight);
435
+
436
+ // Warm dusk light
437
+ const duskLight = new THREE.DirectionalLight(0xDAA520, 0.4);
438
+ duskLight.position.set(-50, 30, 100);
439
+ scene.add(duskLight);
440
+
441
+ // Point light for fireflies area
442
+ const fireflyLight = new THREE.PointLight(0xFFFF80, 0.5, 30);
443
+ fireflyLight.position.set(0, 5, 0);
444
+ scene.add(fireflyLight);
445
+
446
+ // Controls
447
+ document.addEventListener('mousemove', (event) => {
448
+ mouseX = (event.clientX / window.innerWidth) * 2 - 1;
449
+ mouseY = (event.clientY / window.innerHeight) * 2 - 1;
450
+ });
451
+
452
+ document.addEventListener('keydown', (event) => {
453
+ keys[event.code] = true;
454
+ });
455
+
456
+ document.addEventListener('keyup', (event) => {
457
+ keys[event.code] = false;
458
+ });
459
+
460
+ // Animation loop
461
+ let time = 0;
462
+ function animate() {
463
+ requestAnimationFrame(animate);
464
+ time += 0.01;
465
+
466
+ // Update shaders
467
+ skyMaterial.uniforms.time.value = time;
468
+ waterMaterial.uniforms.time.value = time;
469
+
470
+ // Camera controls
471
+ cameraRotationY += (mouseX * 0.5 - cameraRotationY) * 0.05;
472
+ cameraRotationX += (mouseY * 0.3 - cameraRotationX) * 0.05;
473
+ cameraRotationX = Math.max(-Math.PI / 3, Math.min(Math.PI / 3, cameraRotationX));
474
+
475
+ // Movement
476
+ const moveSpeed = 0.3;
477
+ if (keys['ArrowUp'] || keys['KeyW']) {
478
+ camera.position.z -= Math.cos(cameraRotationY) * moveSpeed;
479
+ camera.position.x -= Math.sin(cameraRotationY) * moveSpeed;
480
+ }
481
+ if (keys['ArrowDown'] || keys['KeyS']) {
482
+ camera.position.z += Math.cos(cameraRotationY) * moveSpeed;
483
+ camera.position.x += Math.sin(cameraRotationY) * moveSpeed;
484
+ }
485
+ if (keys['ArrowLeft'] || keys['KeyA']) {
486
+ camera.position.x -= Math.cos(cameraRotationY) * moveSpeed;
487
+ camera.position.z += Math.sin(cameraRotationY) * moveSpeed;
488
+ }
489
+ if (keys['ArrowRight'] || keys['KeyD']) {
490
+ camera.position.x += Math.cos(cameraRotationY) * moveSpeed;
491
+ camera.position.z -= Math.sin(cameraRotationY) * moveSpeed;
492
+ }
493
+
494
+ // Apply camera rotation
495
+ camera.rotation.x = cameraRotationX;
496
+ camera.rotation.y = cameraRotationY;
497
+
498
+ // Animate fireflies rising like embers
499
+ fireflies.forEach((firefly, index) => {
500
+ firefly.position.y += firefly.userData.speed;
501
+ firefly.position.x += Math.sin(time + firefly.userData.phase) * 0.01;
502
+ firefly.position.z += Math.cos(time + firefly.userData.phase) * 0.01;
503
+
504
+ // Reset fireflies that get too high
505
+ if (firefly.position.y > 20) {
506
+ firefly.position.y = 0;
507
+ firefly.position.x = (Math.random() - 0.5) * 100;
508
+ firefly.position.z = (Math.random() - 0.5) * 100;
509
+ }
510
+
511
+ // Flickering effect
512
+ firefly.material.opacity = 0.3 + Math.sin(time * 5 + index) * 0.5;
513
+ });
514
+
515
+ // Gentle pier swaying
516
+ pier.rotation.z = Math.sin(time * 0.5) * 0.02;
517
+
518
+ // Pontoon bobbing (breathing slower)
519
+ pontoon.position.y = -0.3 + Math.sin(time * 0.3) * 0.1;
520
+ pontoon.rotation.x = 0.1 + Math.sin(time * 0.4) * 0.05;
521
+
522
+ // Mast slapping animation (morse code)
523
+ const mast = pontoon.children.find(child => child.geometry?.parameters?.height === 6);
524
+ if (mast) {
525
+ mast.rotation.z = Math.sin(time * 2) * 0.1;
526
+ }
527
+
528
+ renderer.render(scene, camera);
529
+ }
530
+
531
+ // Handle window resize
532
+ window.addEventListener('resize', () => {
533
+ camera.aspect = window.innerWidth / window.innerHeight;
534
+ camera.updateProjectionMatrix();
535
+ renderer.setSize(window.innerWidth, window.innerHeight);
536
+ });
537
+
538
+ // Start animation
539
+ animate();
540
+ </script>
541
+ </body>
542
+ </html>