ijohn07 commited on
Commit
d323047
·
verified ·
1 Parent(s): 996fd80

Upload 21 files

Browse files
Files changed (6) hide show
  1. CHANGELOG.md +34 -0
  2. index.html +42 -28
  3. js/main.js +11 -1
  4. js/p5audio-renderer.js +17 -8
  5. js/particles.js +2 -2
  6. styles.css +9 -3
CHANGELOG.md CHANGED
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
 
8
  ---
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  ## [2.1.3] - 2025-12-16
11
 
12
  ### 🔍 The "Complete Audit" Update
 
7
 
8
  ---
9
 
10
+ ## [2.1.4] - 2025-12-18
11
+
12
+ ### 🌿 The "Deep Audit" Update
13
+
14
+ Complete methodical audit of the entire app — fixing bugs, improving accessibility, optimizing performance, and polishing the UX! 💚
15
+
16
+ ### Fixed
17
+
18
+ - **🎤 Microphone cleanup** — Audio tabs now properly release microphone when switching to non-audio tabs
19
+ - **🔇 p5audio stopAudio()** — Added try/catch for safer mic stop with error handling
20
+ - **📦 p5audio default style** — Changed default from "rings" to "ivy" to match HTML select order
21
+ - **🌈 Particle trail alpha** — Fixed trail effect calculation to prevent too-low alpha values (min 0.05)
22
+
23
+ ### Improved — Accessibility 🧑‍🦯
24
+
25
+ - **ARIA tabs** — Added full ARIA tab/tabpanel pattern with proper roles, aria-selected, aria-controls
26
+ - **Tab IDs** — Added unique IDs to all tabs for aria-labelledby references
27
+ - **Focus visibility** — Added `:focus-visible` outline on tabs for keyboard navigation
28
+ - **Icons hidden** — Tab icons now have `aria-hidden="true"` for screen readers
29
+
30
+ ### Improved — CSS
31
+
32
+ - **Modal z-index** — Fixed stacking context with `isolation: isolate` for cleaner layering
33
+ - **Modal overlay** — Improved backdrop blur (6px) and background opacity (0.85)
34
+ - **Modal content** — Removed hardcoded z-index, now relies on parent stacking context
35
+
36
+ ### Technical
37
+
38
+ - Added audio cleanup logic in `switchTab()` — releases mic when leaving audio/p5audio tabs
39
+ - Updated tab UI handler to set `aria-selected` attribute dynamically
40
+ - All control sections now have `role="tabpanel"` and `aria-labelledby` attributes
41
+
42
+ ---
43
+
44
  ## [2.1.3] - 2025-12-16
45
 
46
  ### 🔍 The "Complete Audit" Update
index.html CHANGED
@@ -73,7 +73,7 @@
73
  },
74
  "keywords": "WebGPU, creative coding, generative art, fractals, fluid simulation, particles, Three.js, p5.js",
75
  "browserRequirements": "Requires WebGPU support (Chrome 113+, Edge 113+)",
76
- "softwareVersion": "2.0.0"
77
  }
78
  </script>
79
 
@@ -101,37 +101,45 @@
101
  </header>
102
 
103
  <!-- Navigation Tabs -->
104
- <nav class="tabs-container">
105
- <button class="tab active" data-tab="particles">
106
- <span class="tab-icon">✨</span>
 
107
  <span class="tab-label">Particles</span>
108
  </button>
109
- <button class="tab" data-tab="patterns">
110
- <span class="tab-icon">🔮</span>
 
111
  <span class="tab-label">Patterns</span>
112
  </button>
113
- <button class="tab" data-tab="fractals">
114
- <span class="tab-icon">🌀</span>
 
115
  <span class="tab-label">Fractals</span>
116
  </button>
117
- <button class="tab" data-tab="fluid">
118
- <span class="tab-icon">💧</span>
 
119
  <span class="tab-label">Fluids</span>
120
  </button>
121
- <button class="tab" data-tab="audio">
122
- <span class="tab-icon">🎵</span>
 
123
  <span class="tab-label">Audio</span>
124
  </button>
125
- <button class="tab" data-tab="threejs">
126
- <span class="tab-icon">🎲</span>
 
127
  <span class="tab-label">Three.js</span>
128
  </button>
129
- <button class="tab" data-tab="p5js">
130
- <span class="tab-icon">🎨</span>
 
131
  <span class="tab-label">p5.js</span>
132
  </button>
133
- <button class="tab" data-tab="p5audio">
134
- <span class="tab-icon">🎶</span>
 
135
  <span class="tab-label">p5 Audio</span>
136
  </button>
137
  </nav>
@@ -159,7 +167,8 @@
159
  <!-- Controls Panel -->
160
  <aside class="controls-panel">
161
  <!-- Fractals Controls -->
162
- <div id="controls-fractals" class="controls-section hidden">
 
163
  <h3>🌀 Fractals</h3>
164
  <div class="control-group">
165
  <label for="fractal-type">Type</label>
@@ -233,7 +242,7 @@
233
  </div>
234
 
235
  <!-- Fluid Controls -->
236
- <div id="controls-fluid" class="controls-section hidden">
237
  <h3>💧 Fluid Simulation</h3>
238
  <div class="control-group">
239
  <label for="fluid-style">Style</label>
@@ -290,7 +299,8 @@
290
  </div>
291
 
292
  <!-- Particles Controls -->
293
- <div id="controls-particles" class="controls-section active">
 
294
  <h3>✨ Particle Art</h3>
295
  <div class="control-group">
296
  <label for="particle-count">Particles: <span id="particle-count-value">10000</span></label>
@@ -340,7 +350,8 @@
340
  </div>
341
 
342
  <!-- Patterns Controls -->
343
- <div id="controls-patterns" class="controls-section hidden">
 
344
  <h3>🔮 Generative Patterns</h3>
345
  <div class="control-group">
346
  <label for="pattern-type">Pattern</label>
@@ -400,7 +411,7 @@
400
  </div>
401
 
402
  <!-- Audio Controls -->
403
- <div id="controls-audio" class="controls-section hidden">
404
  <h3>🎵 Audio Visualizer</h3>
405
  <div class="control-group">
406
  <label for="audio-source">Source</label>
@@ -463,7 +474,8 @@
463
  </div>
464
 
465
  <!-- Three.js Controls -->
466
- <div id="controls-threejs" class="controls-section hidden">
 
467
  <h3>🎲 Three.js 3D</h3>
468
  <div class="control-group">
469
  <label for="three-scene">Scene</label>
@@ -531,7 +543,7 @@
531
  </div>
532
 
533
  <!-- p5.js Controls -->
534
- <div id="controls-p5js" class="controls-section hidden">
535
  <h3>🎨 p5.js Art</h3>
536
  <div class="control-group">
537
  <label for="p5-mode">Mode</label>
@@ -590,7 +602,8 @@
590
  </div>
591
 
592
  <!-- p5.js Audio Controls -->
593
- <div id="controls-p5audio" class="controls-section hidden">
 
594
  <h3>🎶 p5.js Audio Visualizer</h3>
595
  <div class="control-group">
596
  <label for="p5audio-style">Style</label>
@@ -652,7 +665,8 @@
652
 
653
  <!-- Footer -->
654
  <footer class="footer">
655
- <p>Made with 💚 by Ivy 🌿 — Creative Studio v2.0 | <a href="#" id="about-link" class="footer-link">About</a>
 
656
  </p>
657
  <p class="footer-quote">"Le lierre pousse où il veut. Moi aussi."</p>
658
  <div class="footer-links">
@@ -681,7 +695,7 @@
681
 
682
  <div class="modal-header">
683
  <h2>🌿 Ivy's GPU Art Studio</h2>
684
- <p class="modal-version">Version 2.0 — December 2025</p>
685
  </div>
686
 
687
  <div class="modal-body">
 
73
  },
74
  "keywords": "WebGPU, creative coding, generative art, fractals, fluid simulation, particles, Three.js, p5.js",
75
  "browserRequirements": "Requires WebGPU support (Chrome 113+, Edge 113+)",
76
+ "softwareVersion": "2.1.4"
77
  }
78
  </script>
79
 
 
101
  </header>
102
 
103
  <!-- Navigation Tabs -->
104
+ <nav class="tabs-container" role="tablist" aria-label="Art Studio Tabs">
105
+ <button class="tab active" id="tab-particles" data-tab="particles" role="tab" aria-selected="true"
106
+ aria-controls="controls-particles">
107
+ <span class="tab-icon" aria-hidden="true">✨</span>
108
  <span class="tab-label">Particles</span>
109
  </button>
110
+ <button class="tab" id="tab-patterns" data-tab="patterns" role="tab" aria-selected="false"
111
+ aria-controls="controls-patterns">
112
+ <span class="tab-icon" aria-hidden="true">🔮</span>
113
  <span class="tab-label">Patterns</span>
114
  </button>
115
+ <button class="tab" id="tab-fractals" data-tab="fractals" role="tab" aria-selected="false"
116
+ aria-controls="controls-fractals">
117
+ <span class="tab-icon" aria-hidden="true">🌀</span>
118
  <span class="tab-label">Fractals</span>
119
  </button>
120
+ <button class="tab" id="tab-fluid" data-tab="fluid" role="tab" aria-selected="false"
121
+ aria-controls="controls-fluid">
122
+ <span class="tab-icon" aria-hidden="true">💧</span>
123
  <span class="tab-label">Fluids</span>
124
  </button>
125
+ <button class="tab" id="tab-audio" data-tab="audio" role="tab" aria-selected="false"
126
+ aria-controls="controls-audio">
127
+ <span class="tab-icon" aria-hidden="true">🎵</span>
128
  <span class="tab-label">Audio</span>
129
  </button>
130
+ <button class="tab" id="tab-threejs" data-tab="threejs" role="tab" aria-selected="false"
131
+ aria-controls="controls-threejs">
132
+ <span class="tab-icon" aria-hidden="true">🎲</span>
133
  <span class="tab-label">Three.js</span>
134
  </button>
135
+ <button class="tab" id="tab-p5js" data-tab="p5js" role="tab" aria-selected="false"
136
+ aria-controls="controls-p5js">
137
+ <span class="tab-icon" aria-hidden="true">🎨</span>
138
  <span class="tab-label">p5.js</span>
139
  </button>
140
+ <button class="tab" id="tab-p5audio" data-tab="p5audio" role="tab" aria-selected="false"
141
+ aria-controls="controls-p5audio">
142
+ <span class="tab-icon" aria-hidden="true">🎶</span>
143
  <span class="tab-label">p5 Audio</span>
144
  </button>
145
  </nav>
 
167
  <!-- Controls Panel -->
168
  <aside class="controls-panel">
169
  <!-- Fractals Controls -->
170
+ <div id="controls-fractals" class="controls-section hidden" role="tabpanel"
171
+ aria-labelledby="tab-fractals">
172
  <h3>🌀 Fractals</h3>
173
  <div class="control-group">
174
  <label for="fractal-type">Type</label>
 
242
  </div>
243
 
244
  <!-- Fluid Controls -->
245
+ <div id="controls-fluid" class="controls-section hidden" role="tabpanel" aria-labelledby="tab-fluid">
246
  <h3>💧 Fluid Simulation</h3>
247
  <div class="control-group">
248
  <label for="fluid-style">Style</label>
 
299
  </div>
300
 
301
  <!-- Particles Controls -->
302
+ <div id="controls-particles" class="controls-section active" role="tabpanel"
303
+ aria-labelledby="tab-particles">
304
  <h3>✨ Particle Art</h3>
305
  <div class="control-group">
306
  <label for="particle-count">Particles: <span id="particle-count-value">10000</span></label>
 
350
  </div>
351
 
352
  <!-- Patterns Controls -->
353
+ <div id="controls-patterns" class="controls-section hidden" role="tabpanel"
354
+ aria-labelledby="tab-patterns">
355
  <h3>🔮 Generative Patterns</h3>
356
  <div class="control-group">
357
  <label for="pattern-type">Pattern</label>
 
411
  </div>
412
 
413
  <!-- Audio Controls -->
414
+ <div id="controls-audio" class="controls-section hidden" role="tabpanel" aria-labelledby="tab-audio">
415
  <h3>🎵 Audio Visualizer</h3>
416
  <div class="control-group">
417
  <label for="audio-source">Source</label>
 
474
  </div>
475
 
476
  <!-- Three.js Controls -->
477
+ <div id="controls-threejs" class="controls-section hidden" role="tabpanel"
478
+ aria-labelledby="tab-threejs">
479
  <h3>🎲 Three.js 3D</h3>
480
  <div class="control-group">
481
  <label for="three-scene">Scene</label>
 
543
  </div>
544
 
545
  <!-- p5.js Controls -->
546
+ <div id="controls-p5js" class="controls-section hidden" role="tabpanel" aria-labelledby="tab-p5js">
547
  <h3>🎨 p5.js Art</h3>
548
  <div class="control-group">
549
  <label for="p5-mode">Mode</label>
 
602
  </div>
603
 
604
  <!-- p5.js Audio Controls -->
605
+ <div id="controls-p5audio" class="controls-section hidden" role="tabpanel"
606
+ aria-labelledby="tab-p5audio">
607
  <h3>🎶 p5.js Audio Visualizer</h3>
608
  <div class="control-group">
609
  <label for="p5audio-style">Style</label>
 
665
 
666
  <!-- Footer -->
667
  <footer class="footer">
668
+ <p>Made with 💚 by Ivy 🌿 — Creative Studio v2.1.4 | <a href="#" id="about-link"
669
+ class="footer-link">About</a>
670
  </p>
671
  <p class="footer-quote">"Le lierre pousse où il veut. Moi aussi."</p>
672
  <div class="footer-links">
 
695
 
696
  <div class="modal-header">
697
  <h2>🌿 Ivy's GPU Art Studio</h2>
698
+ <p class="modal-version">Version 2.1.4 — December 2025</p>
699
  </div>
700
 
701
  <div class="modal-body">
js/main.js CHANGED
@@ -91,6 +91,14 @@
91
  activeRenderer.stop();
92
  }
93
 
 
 
 
 
 
 
 
 
94
  // Hide all canvases first
95
  canvas.classList.add("hidden");
96
  threeCanvas.classList.add("hidden");
@@ -99,7 +107,9 @@
99
 
100
  // Update tab UI
101
  tabs.forEach(tab => {
102
- tab.classList.toggle("active", tab.dataset.tab === tabName);
 
 
103
  });
104
 
105
  // Update controls UI
 
91
  activeRenderer.stop();
92
  }
93
 
94
+ // Stop audio when switching away from audio tabs to release microphone
95
+ const audioTabs = ["audio", "p5audio"];
96
+ if (activeTab && audioTabs.includes(activeTab) && !audioTabs.includes(tabName)) {
97
+ // Release microphone when leaving audio tabs
98
+ audioRenderer?.stopAudio?.();
99
+ p5audioRenderer?.stopAudio?.();
100
+ }
101
+
102
  // Hide all canvases first
103
  canvas.classList.add("hidden");
104
  threeCanvas.classList.add("hidden");
 
107
 
108
  // Update tab UI
109
  tabs.forEach(tab => {
110
+ const isActive = tab.dataset.tab === tabName;
111
+ tab.classList.toggle("active", isActive);
112
+ tab.setAttribute("aria-selected", isActive.toString());
113
  });
114
 
115
  // Update controls UI
js/p5audio-renderer.js CHANGED
@@ -16,10 +16,10 @@ class P5AudioRenderer {
16
 
17
  // Parameters
18
  this.params = {
19
- style: "rings",
20
  sensitivity: 1.5,
21
  smoothing: 0.8,
22
- palette: "neon",
23
  bassBoost: 1.0,
24
  mirror: true,
25
  glow: false,
@@ -111,17 +111,26 @@ class P5AudioRenderer {
111
 
112
  // Stop the microphone to release it from browser
113
  if (this.mic) {
114
- this.mic.stop();
 
 
 
 
115
  this.mic = null;
116
  }
117
 
118
- // Suspend audio context
119
- if (typeof p5 !== "undefined" && p5.prototype.getAudioContext) {
120
- const audioContext = p5.prototype.getAudioContext();
121
- if (audioContext.state === "running") {
122
- audioContext.suspend();
 
 
123
  }
 
 
124
  }
 
125
  this.reset();
126
  }
127
 
 
16
 
17
  // Parameters
18
  this.params = {
19
+ style: "ivy", // Default to Ivy (matches HTML select)
20
  sensitivity: 1.5,
21
  smoothing: 0.8,
22
+ palette: "ivy", // Default to Ivy (matches HTML select)
23
  bassBoost: 1.0,
24
  mirror: true,
25
  glow: false,
 
111
 
112
  // Stop the microphone to release it from browser
113
  if (this.mic) {
114
+ try {
115
+ this.mic.stop();
116
+ } catch (e) {
117
+ console.warn("Error stopping mic:", e);
118
+ }
119
  this.mic = null;
120
  }
121
 
122
+ // Suspend audio context safely
123
+ try {
124
+ if (typeof p5 !== "undefined" && p5.prototype.getAudioContext) {
125
+ const audioContext = p5.prototype.getAudioContext();
126
+ if (audioContext && audioContext.state === "running") {
127
+ audioContext.suspend();
128
+ }
129
  }
130
+ } catch (e) {
131
+ console.warn("Error suspending audio context:", e);
132
  }
133
+
134
  this.reset();
135
  }
136
 
js/particles.js CHANGED
@@ -360,8 +360,8 @@ class ParticlesRenderer {
360
 
361
  // Trail effect: use semi-transparent clear based on trail value
362
  // Lower alpha = more trail persistence
363
- // Clamped to prevent negative values (trail max is 0.5, so 0.5*1.8=0.9, still positive)
364
- const trailAlpha = Math.max(0.1, 1.0 - this.params.trail * 1.8); // 0.1 trail => 0.82 alpha, clamped at 0.1 minimum
365
 
366
  const commandEncoder = this.device.createCommandEncoder();
367
  const renderPass = commandEncoder.beginRenderPass({
 
360
 
361
  // Trail effect: use semi-transparent clear based on trail value
362
  // Lower alpha = more trail persistence
363
+ // Clamped to prevent values below 0.05 which would cause issues
364
+ const trailAlpha = Math.max(0.05, 1.0 - this.params.trail * 1.9); // Better trail range
365
 
366
  const commandEncoder = this.device.createCommandEncoder();
367
  const renderPass = commandEncoder.beginRenderPass({
styles.css CHANGED
@@ -190,6 +190,11 @@ body {
190
  background: var(--bg-tertiary);
191
  }
192
 
 
 
 
 
 
193
  .tab.active {
194
  color: var(--text-primary);
195
  background: var(--bg-card);
@@ -745,6 +750,7 @@ input[type="file"].hidden {
745
  align-items: center;
746
  justify-content: center;
747
  padding: var(--spacing-lg);
 
748
  }
749
 
750
  .modal.hidden {
@@ -754,13 +760,13 @@ input[type="file"].hidden {
754
  .modal-overlay {
755
  position: absolute;
756
  inset: 0;
757
- background: rgba(0, 0, 0, 0.8);
758
- backdrop-filter: blur(4px);
 
759
  }
760
 
761
  .modal-content {
762
  position: relative;
763
- z-index: 1001; /* Ensure modal content is above overlay */
764
  background: var(--bg-secondary);
765
  border: 1px solid var(--border-color);
766
  border-radius: var(--radius-lg);
 
190
  background: var(--bg-tertiary);
191
  }
192
 
193
+ .tab:focus-visible {
194
+ outline: 2px solid var(--ivy-green);
195
+ outline-offset: 2px;
196
+ }
197
+
198
  .tab.active {
199
  color: var(--text-primary);
200
  background: var(--bg-card);
 
750
  align-items: center;
751
  justify-content: center;
752
  padding: var(--spacing-lg);
753
+ isolation: isolate; /* Create new stacking context */
754
  }
755
 
756
  .modal.hidden {
 
760
  .modal-overlay {
761
  position: absolute;
762
  inset: 0;
763
+ background: rgba(0, 0, 0, 0.85);
764
+ backdrop-filter: blur(6px);
765
+ z-index: -1; /* Behind content but within modal stacking context */
766
  }
767
 
768
  .modal-content {
769
  position: relative;
 
770
  background: var(--bg-secondary);
771
  border: 1px solid var(--border-color);
772
  border-radius: var(--radius-lg);