watercooler 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/app.js +168 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "watercooler",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "A beautiful 3D visualization of your mailbox messages as a village of coworkers",
5
5
  "type": "module",
6
6
  "main": "server.ts",
package/public/app.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import * as THREE from 'three';
2
2
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3
+ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
4
+ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
5
+ import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
3
6
 
4
7
  // State
5
8
  let config = { user: '', mailbox: '', avatar: null };
@@ -36,6 +39,8 @@ let holoSphere = null;
36
39
  let holoParticles = null;
37
40
  let glowLights = [];
38
41
  let floatingParticles = [];
42
+ let composer = null;
43
+ let waterMesh = null;
39
44
 
40
45
  // Initialize Three.js
41
46
  function init() {
@@ -99,9 +104,27 @@ function init() {
99
104
  const hemiLight = new THREE.HemisphereLight(0x4fd1c5, 0x1a3a3a, 0.4);
100
105
  scene.add(hemiLight);
101
106
 
107
+ // Additional accent lights for bloom effect
108
+ // Center glow from holographic sphere area
109
+ const centerGlow = new THREE.PointLight(0x4fd1c5, 0.6, 50);
110
+ centerGlow.position.set(0, PLATFORM_HEIGHT + 15, 0);
111
+ scene.add(centerGlow);
112
+
113
+ // Edge accent lights
114
+ const edgeLight1 = new THREE.PointLight(0x88ffdd, 0.4, 30);
115
+ edgeLight1.position.set(30, PLATFORM_HEIGHT + 10, 30);
116
+ scene.add(edgeLight1);
117
+
118
+ const edgeLight2 = new THREE.PointLight(0x88ffdd, 0.4, 30);
119
+ edgeLight2.position.set(-30, PLATFORM_HEIGHT + 10, -30);
120
+ scene.add(edgeLight2);
121
+
102
122
  // === Platform ===
103
123
  createPlatform();
104
124
 
125
+ // === Reflective Water Surface ===
126
+ createReflectiveWater();
127
+
105
128
  // === Glass Walls ===
106
129
  createGlassWalls();
107
130
 
@@ -117,6 +140,9 @@ function init() {
117
140
  // === Floating Particles ===
118
141
  createFloatingParticles();
119
142
 
143
+ // === Background Stars ===
144
+ createBackgroundStars();
145
+
120
146
  window.addEventListener('resize', onWindowResize);
121
147
 
122
148
  // Raycaster for desk clicks
@@ -136,6 +162,9 @@ function init() {
136
162
  setTimeout(onWindowResize, 100);
137
163
  });
138
164
 
165
+ // === Post Processing ===
166
+ setupPostProcessing();
167
+
139
168
  animate();
140
169
  }
141
170
 
@@ -198,6 +227,56 @@ function createPlatform() {
198
227
  scene.add(ground);
199
228
  }
200
229
 
230
+ function createReflectiveWater() {
231
+ // Reflective water surface below the platform
232
+ const waterSize = PLATFORM_SIZE * 1.5;
233
+ const waterGeo = new THREE.PlaneGeometry(waterSize, waterSize, 64, 64);
234
+
235
+ // Create a custom shader material for reflective water effect
236
+ const waterMat = new THREE.MeshPhysicalMaterial({
237
+ color: 0x0d3333,
238
+ metalness: 0.9,
239
+ roughness: 0.1,
240
+ transparent: true,
241
+ opacity: 0.85,
242
+ transmission: 0.3,
243
+ thickness: 0.5,
244
+ clearcoat: 1.0,
245
+ clearcoatRoughness: 0.1,
246
+ side: THREE.DoubleSide
247
+ });
248
+
249
+ waterMesh = new THREE.Mesh(waterGeo, waterMat);
250
+ waterMesh.rotation.x = -Math.PI / 2;
251
+ waterMesh.position.y = -0.5;
252
+ waterMesh.receiveShadow = true;
253
+ scene.add(waterMesh);
254
+
255
+ // Add subtle ripple effect using vertex displacement
256
+ const positions = waterMesh.geometry.attributes.position;
257
+ const initialPositions = positions.array.slice();
258
+ waterMesh.userData.initialPositions = initialPositions;
259
+ waterMesh.userData.ripplePhase = 0;
260
+ }
261
+
262
+ function setupPostProcessing() {
263
+ // Setup EffectComposer for bloom
264
+ composer = new EffectComposer(renderer);
265
+
266
+ // Add render pass
267
+ const renderPass = new RenderPass(scene, camera);
268
+ composer.addPass(renderPass);
269
+
270
+ // Add bloom pass
271
+ const bloomPass = new UnrealBloomPass(
272
+ new THREE.Vector2(window.innerWidth, window.innerHeight),
273
+ 0.8, // strength
274
+ 0.4, // radius
275
+ 0.75 // threshold
276
+ );
277
+ composer.addPass(bloomPass);
278
+ }
279
+
201
280
  function createGlassWalls() {
202
281
  const glassMat = new THREE.MeshPhysicalMaterial({
203
282
  color: 0x88cccc,
@@ -491,6 +570,48 @@ function createFloatingParticles() {
491
570
  floatingParticles.push(particles);
492
571
  }
493
572
 
573
+ function createBackgroundStars() {
574
+ // Distant stars/sparkles in the background
575
+ const starCount = 200;
576
+ const positions = new Float32Array(starCount * 3);
577
+ const sizes = new Float32Array(starCount);
578
+
579
+ for (let i = 0; i < starCount; i++) {
580
+ // Place stars far outside the platform
581
+ const theta = Math.random() * Math.PI * 2;
582
+ const phi = Math.acos(2 * Math.random() - 1);
583
+ const radius = 100 + Math.random() * 150;
584
+
585
+ positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
586
+ positions[i * 3 + 1] = 20 + Math.random() * 100;
587
+ positions[i * 3 + 2] = radius * Math.sin(phi) * Math.sin(theta);
588
+
589
+ sizes[i] = 0.5 + Math.random() * 1.5;
590
+ }
591
+
592
+ const geometry = new THREE.BufferGeometry();
593
+ geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
594
+ geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
595
+
596
+ const material = new THREE.PointsMaterial({
597
+ color: 0xaaddff,
598
+ size: 1.0,
599
+ transparent: true,
600
+ opacity: 0.6,
601
+ blending: THREE.AdditiveBlending,
602
+ sizeAttenuation: true
603
+ });
604
+
605
+ const stars = new THREE.Points(geometry, material);
606
+ scene.add(stars);
607
+
608
+ // Animate stars with twinkle effect
609
+ stars.userData.twinklePhase = Math.random() * Math.PI * 2;
610
+
611
+ // Add to floatingParticles for animation
612
+ floatingParticles.push(stars);
613
+ }
614
+
494
615
  function createAgentDesk(name, position, toolName = null) {
495
616
  const color = getAgentColor(name);
496
617
  const group = new THREE.Group();
@@ -946,12 +1067,23 @@ function animate() {
946
1067
  }
947
1068
 
948
1069
  // Animate floating particles
949
- floatingParticles.forEach(particles => {
1070
+ floatingParticles.forEach((particles, index) => {
950
1071
  const positions = particles.geometry.attributes.position.array;
951
- for (let i = 0; i < positions.length; i += 3) {
952
- positions[i + 1] += Math.sin(time + positions[i] * 0.1) * 0.003;
1072
+
1073
+ if (particles.userData.twinklePhase !== undefined) {
1074
+ // Star twinkling effect
1075
+ const twinkle = Math.sin(time * 2 + particles.userData.twinklePhase) * 0.3 + 0.7;
1076
+ particles.material.opacity = 0.4 + twinkle * 0.4;
1077
+
1078
+ // Slowly rotate stars
1079
+ particles.rotation.y = time * 0.02;
1080
+ } else {
1081
+ // Regular floating particles
1082
+ for (let i = 0; i < positions.length; i += 3) {
1083
+ positions[i + 1] += Math.sin(time + positions[i] * 0.1) * 0.003;
1084
+ }
1085
+ particles.geometry.attributes.position.needsUpdate = true;
953
1086
  }
954
- particles.geometry.attributes.position.needsUpdate = true;
955
1087
  });
956
1088
 
957
1089
  // Subtle glow pulse on lamps
@@ -960,8 +1092,34 @@ function animate() {
960
1092
  item.light.intensity = item.baseIntensity * pulse;
961
1093
  });
962
1094
 
1095
+ // Animate water ripples
1096
+ if (waterMesh && waterMesh.userData.initialPositions) {
1097
+ const positions = waterMesh.geometry.attributes.position;
1098
+ const initialPositions = waterMesh.userData.initialPositions;
1099
+
1100
+ for (let i = 0; i < positions.count; i++) {
1101
+ const x = initialPositions[i * 3];
1102
+ const y = initialPositions[i * 3 + 1];
1103
+
1104
+ // Create gentle ripple effect
1105
+ const distance = Math.sqrt(x * x + y * y);
1106
+ const wave1 = Math.sin(distance * 0.3 - time * 0.8) * 0.15;
1107
+ const wave2 = Math.sin(x * 0.2 + time * 0.5) * 0.1;
1108
+ const wave3 = Math.cos(y * 0.15 + time * 0.3) * 0.08;
1109
+
1110
+ positions.setZ(i, wave1 + wave2 + wave3);
1111
+ }
1112
+ positions.needsUpdate = true;
1113
+ }
1114
+
963
1115
  controls.update();
964
- renderer.render(scene, camera);
1116
+
1117
+ // Use composer for bloom effect if available, otherwise standard renderer
1118
+ if (composer) {
1119
+ composer.render();
1120
+ } else {
1121
+ renderer.render(scene, camera);
1122
+ }
965
1123
  }
966
1124
 
967
1125
  function onWindowResize() {
@@ -971,6 +1129,11 @@ function onWindowResize() {
971
1129
  camera.updateProjectionMatrix();
972
1130
  renderer.setSize(width, height);
973
1131
 
1132
+ // Resize composer for bloom effect
1133
+ if (composer) {
1134
+ composer.setSize(width, height);
1135
+ }
1136
+
974
1137
  if (width < 768) {
975
1138
  camera.position.y = Math.max(camera.position.y, 40);
976
1139
  camera.position.z = Math.max(camera.position.z, 50);