p5-phone 1.4.4 → 1.5.0
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.
- package/README.md +121 -5
- package/dist/p5-phone.js +105 -2
- package/dist/p5-phone.min.js +3 -3
- package/examples/Phone Sensor Examples/microphone/01_mic_level/index.html +2 -2
- package/examples/Phone Sensor Examples/movement/01_orientation_basic/index.html +1 -1
- package/examples/Phone Sensor Examples/movement/02_rotational_velocity/index.html +1 -1
- package/examples/Phone Sensor Examples/movement/03_acceleration/index.html +1 -1
- package/examples/Phone Sensor Examples/sound/01_dual_audio/index.html +1 -1
- package/examples/Phone Sensor Examples/sound/02_volume_touches/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/01_touch_basic/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/02_touch_zones/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/03_touch_count/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/04_touch_distance/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/05_touch_angle/index.html +1 -1
- package/examples/Phone Sensor Examples/vibration/01_haptic_feedback/README.md +51 -0
- package/examples/Phone Sensor Examples/vibration/01_haptic_feedback/index.html +28 -0
- package/examples/Phone Sensor Examples/vibration/01_haptic_feedback/sketch.js +177 -0
- package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/index.html +1 -1
- package/examples/Phone and Gif/collision/index.html +1 -1
- package/examples/Phone and Gif/fetch/index.html +1 -1
- package/examples/Phone and Gif/fly/index.html +1 -1
- package/examples/Phone and Gif/roll/index.html +1 -1
- package/examples/UXcompare/button-vs-movement/index.html +1 -1
- package/examples/UXcompare/button-vs-orientation/index.html +1 -1
- package/examples/UXcompare/button-vs-shake/index.html +1 -1
- package/examples/UXcompare/gyroscope-demo/index.html +1 -1
- package/examples/UXcompare/microphone-demo/index.html +1 -1
- package/examples/UXcompare/slider-vs-angle/index.html +1 -1
- package/examples/UXcompare/slider-vs-distance/index.html +1 -1
- package/examples/UXcompare/slider-vs-microphone/index.html +1 -1
- package/examples/UXcompare/slider-vs-touches/index.html +1 -1
- package/examples/UXcompare/sliders-vs-acceleration/index.html +1 -1
- package/examples/UXcompare/sliders-vs-rotation/index.html +1 -1
- package/examples/blankTemplate/index.html +1 -1
- package/examples/homepage/index.html +805 -88
- package/package.json +5 -5
- package/src/p5-phone.js +105 -2
- package/src/permissionMic.js +0 -240
- package/src/permissionsGesture.js +0 -213
- package/src/permissionsGyro.js +0 -246
package/README.md
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
# p5-phone
|
|
2
|
-

|
|
3
|
+
*Illustration by [Angela Torchio](https://angelatorchio.com/)*
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## [Link for Interactive Examples](https://digitalfuturesocadu.github.io/P5-Phone-Interactions/examples/homepage)
|
|
5
|
+
## [Link for Interactive Examples](https://npuckett.github.io/p5-phone/examples/homepage)
|
|
7
6
|
|
|
8
7
|
# Overview
|
|
9
8
|
P5.js on mobile provides unique opportunities and challenges. The main P5 framework does an excellent job of making it easy to read data from various phone inputs and sensors, however it doesn't deal with the realities of contemporary browser's built in gestures and security protocols.
|
|
10
9
|
That's where this library comes in:
|
|
11
10
|
|
|
12
|
-
- Simplifies accessing phone hardware from the browser (accelerometers, gyroscopes, microphone)
|
|
11
|
+
- Simplifies accessing phone hardware from the browser (accelerometers, gyroscopes, microphone, vibration motor)
|
|
13
12
|
- Simplifies disabling default phone gestures (Zoom, refresh, back, etc)
|
|
14
13
|
- Simplifies enabling audio output
|
|
15
14
|
- Simplifies using an on-screen console to display errors and debug info
|
|
@@ -61,6 +60,7 @@ This library simplifies access to the following p5.js mobile sensor and audio co
|
|
|
61
60
|
- [Motion Sensor Activation](#motion-sensor-activation)
|
|
62
61
|
- [Microphone Activation](#microphone-activation)
|
|
63
62
|
- [Sound Output Activation](#sound-output-activation)
|
|
63
|
+
- [Vibration Motor (Android Only)](#vibration-motor-android-only)
|
|
64
64
|
- [Debug System](#debug-system)
|
|
65
65
|
|
|
66
66
|
### CDN (Recommended)
|
|
@@ -192,10 +192,17 @@ enableMicButton(text) // Button-based microphone activation
|
|
|
192
192
|
enableSoundTap(message) // Tap anywhere to enable sound playback
|
|
193
193
|
enableSoundButton(text) // Button-based sound activation
|
|
194
194
|
|
|
195
|
+
// Vibration motor (Android only)
|
|
196
|
+
enableVibrationTap(message) // Tap anywhere to enable vibration
|
|
197
|
+
enableVibrationButton(text) // Button-based vibration activation
|
|
198
|
+
vibrate(pattern) // Trigger vibration (duration or pattern array)
|
|
199
|
+
stopVibration() // Stop any ongoing vibration
|
|
200
|
+
|
|
195
201
|
// Status variables (check these in your code)
|
|
196
202
|
window.sensorsEnabled // Boolean: true when motion sensors are active
|
|
197
203
|
window.micEnabled // Boolean: true when microphone is active
|
|
198
204
|
window.soundEnabled // Boolean: true when sound output is active
|
|
205
|
+
window.vibrationEnabled // Boolean: true when vibration is available (Android only)
|
|
199
206
|
|
|
200
207
|
// Debug system (enhanced in v1.4.0)
|
|
201
208
|
showDebug() // Show on-screen debug panel with automatic error catching
|
|
@@ -226,6 +233,7 @@ this.enableGyroTap('Tap to start');
|
|
|
226
233
|
- `window.sensorsEnabled` - Boolean indicating if motion sensors are active
|
|
227
234
|
- `window.micEnabled` - Boolean indicating if microphone is active
|
|
228
235
|
- `window.soundEnabled` - Boolean indicating if sound output is active
|
|
236
|
+
- `window.vibrationEnabled` - Boolean indicating if vibration is available (Android only)
|
|
229
237
|
|
|
230
238
|
**Usage:**
|
|
231
239
|
```javascript
|
|
@@ -245,6 +253,11 @@ function draw() {
|
|
|
245
253
|
// Safe to play sounds
|
|
246
254
|
mySound.play();
|
|
247
255
|
}
|
|
256
|
+
|
|
257
|
+
if (window.vibrationEnabled) {
|
|
258
|
+
// Safe to use vibration (Android only)
|
|
259
|
+
vibrate(50);
|
|
260
|
+
}
|
|
248
261
|
}
|
|
249
262
|
|
|
250
263
|
// You can also use them for conditional UI
|
|
@@ -454,6 +467,109 @@ function mousePressed() {
|
|
|
454
467
|
}
|
|
455
468
|
```
|
|
456
469
|
|
|
470
|
+
### Vibration Motor (Android Only)
|
|
471
|
+
|
|
472
|
+
**Purpose:** Access the device's vibration motor for haptic feedback and tactile interactions.
|
|
473
|
+
|
|
474
|
+
**⚠️ Platform Support:**
|
|
475
|
+
- ✅ **Android** - Full support in Chrome and most Android browsers
|
|
476
|
+
- ❌ **iOS** - Not supported (Vibration API not available on iOS devices)
|
|
477
|
+
|
|
478
|
+
**Important:** The vibration feature will automatically detect if the device supports vibration. On iOS or unsupported devices, `window.vibrationEnabled` will be `false` and vibration calls will be safely ignored with console warnings.
|
|
479
|
+
|
|
480
|
+
**Commands:**
|
|
481
|
+
- `enableVibrationTap(message)` - Tap anywhere on screen to enable vibration
|
|
482
|
+
- `enableVibrationButton(text)` - Creates a button with custom text to enable vibration
|
|
483
|
+
- `vibrate(pattern)` - Trigger vibration with a duration (ms) or pattern array
|
|
484
|
+
- `stopVibration()` - Stop any ongoing vibration
|
|
485
|
+
|
|
486
|
+
**Usage:**
|
|
487
|
+
```javascript
|
|
488
|
+
function setup() {
|
|
489
|
+
createCanvas(windowWidth, windowHeight);
|
|
490
|
+
|
|
491
|
+
// Enable vibration with tap (Android only)
|
|
492
|
+
enableVibrationTap('Tap to enable vibration');
|
|
493
|
+
|
|
494
|
+
// Or use a button
|
|
495
|
+
// enableVibrationButton('Enable Haptics');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function draw() {
|
|
499
|
+
background(220);
|
|
500
|
+
|
|
501
|
+
if (window.vibrationEnabled) {
|
|
502
|
+
text('Vibration ready! Tap anywhere', 20, 20);
|
|
503
|
+
} else {
|
|
504
|
+
text('Vibration not available', 20, 20);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function mousePressed() {
|
|
509
|
+
if (window.vibrationEnabled) {
|
|
510
|
+
// Simple vibration - 50ms pulse
|
|
511
|
+
vibrate(50);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Vibration Patterns:**
|
|
517
|
+
```javascript
|
|
518
|
+
// Single vibration (duration in milliseconds)
|
|
519
|
+
vibrate(100); // Vibrate for 100ms
|
|
520
|
+
|
|
521
|
+
// Pattern: [vibrate, pause, vibrate, pause, ...]
|
|
522
|
+
vibrate([100, 50, 100]); // Short-short pattern
|
|
523
|
+
vibrate([200, 100, 200, 100, 200]); // Triple pulse
|
|
524
|
+
vibrate([50, 50, 50, 50, 500]); // Quick taps then long
|
|
525
|
+
|
|
526
|
+
// Stop any ongoing vibration
|
|
527
|
+
stopVibration();
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**Common Use Cases:**
|
|
531
|
+
```javascript
|
|
532
|
+
// Haptic feedback for button presses
|
|
533
|
+
function mousePressed() {
|
|
534
|
+
if (window.vibrationEnabled) {
|
|
535
|
+
vibrate(20); // Quick tap feedback
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Touch zones with different haptic patterns
|
|
540
|
+
function touchStarted() {
|
|
541
|
+
if (window.vibrationEnabled) {
|
|
542
|
+
if (mouseX < width/2) {
|
|
543
|
+
vibrate(50); // Left side - short pulse
|
|
544
|
+
} else {
|
|
545
|
+
vibrate([50, 30, 50]); // Right side - double pulse
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Collision detection
|
|
552
|
+
function checkCollision() {
|
|
553
|
+
if (collision && window.vibrationEnabled) {
|
|
554
|
+
vibrate([100, 50, 100, 50, 200]); // Alert pattern
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Game events
|
|
559
|
+
function gameOver() {
|
|
560
|
+
if (window.vibrationEnabled) {
|
|
561
|
+
vibrate(500); // Long vibration for game over
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Best Practices:**
|
|
567
|
+
- Use short vibrations (20-100ms) for subtle feedback
|
|
568
|
+
- Use patterns for more complex haptic responses
|
|
569
|
+
- Always check `window.vibrationEnabled` before calling `vibrate()`
|
|
570
|
+
- Don't overuse - vibration can quickly drain battery
|
|
571
|
+
- Test on Android devices as iOS doesn't support vibration
|
|
572
|
+
|
|
457
573
|
### Debug System
|
|
458
574
|
|
|
459
575
|
**Purpose:** Essential on-screen debugging system for mobile development where traditional browser dev tools aren't accessible. Provides automatic error catching, timestamped logging, and color-coded messages.
|
package/dist/p5-phone.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.
|
|
2
|
+
* p5-phone v1.5.0
|
|
3
3
|
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
|
-
* https://github.com/
|
|
4
|
+
* https://github.com/npuckett/p5-phone
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) 2025 Nick Puckett
|
|
7
7
|
* Released under the MIT License
|
|
@@ -85,6 +85,7 @@ window.sensorsEnabled = false;
|
|
|
85
85
|
window.micEnabled = false;
|
|
86
86
|
window.soundEnabled = false;
|
|
87
87
|
window.gesturesLocked = false;
|
|
88
|
+
window.vibrationEnabled = false;
|
|
88
89
|
|
|
89
90
|
// Internal state
|
|
90
91
|
let _permissionsInitialized = false;
|
|
@@ -176,6 +177,30 @@ function enableSoundTap(message = 'Tap screen to enable sound') {
|
|
|
176
177
|
});
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Enable vibration motor with a button interface
|
|
182
|
+
* Creates a start button that user must click
|
|
183
|
+
* Note: Vibration API is supported on Android, but not iOS
|
|
184
|
+
*/
|
|
185
|
+
function enableVibrationButton(buttonText = 'ENABLE VIBRATION', statusText = 'Enabling vibration...') {
|
|
186
|
+
_createPermissionButton(buttonText, statusText, async () => {
|
|
187
|
+
await _requestVibrationPermission();
|
|
188
|
+
console.log('✅ Vibration enabled via button');
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Enable vibration motor with tap-to-start
|
|
194
|
+
* User taps anywhere on screen to enable
|
|
195
|
+
* Note: Vibration API is supported on Android, but not iOS
|
|
196
|
+
*/
|
|
197
|
+
function enableVibrationTap(message = 'Tap screen to enable vibration') {
|
|
198
|
+
_createTapToEnable(message, async () => {
|
|
199
|
+
await _requestVibrationPermission();
|
|
200
|
+
console.log('✅ Vibration enabled via tap');
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
179
204
|
/**
|
|
180
205
|
* Enable both motion sensors and microphone with a button interface
|
|
181
206
|
* Creates a start button that user must click to enable both
|
|
@@ -200,6 +225,39 @@ function enableAllTap(message = 'Tap screen to enable motion sensors & microphon
|
|
|
200
225
|
});
|
|
201
226
|
}
|
|
202
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Trigger vibration on device
|
|
230
|
+
* @param {number|number[]} pattern - Duration in ms or pattern array [vibrate, pause, vibrate, ...]
|
|
231
|
+
*
|
|
232
|
+
* Examples:
|
|
233
|
+
* vibrate(200); // Single 200ms vibration
|
|
234
|
+
* vibrate([100, 50, 100]); // Pattern: vibrate 100ms, pause 50ms, vibrate 100ms
|
|
235
|
+
*
|
|
236
|
+
* Note: Only works if vibrationEnabled is true and device supports vibration
|
|
237
|
+
*/
|
|
238
|
+
function vibrate(pattern) {
|
|
239
|
+
if (!window.vibrationEnabled) {
|
|
240
|
+
console.warn('⚠️ Vibration not enabled. Call enableVibrationTap() or enableVibrationButton() first.');
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!navigator.vibrate) {
|
|
245
|
+
console.warn('⚠️ Vibration API not supported on this device');
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return navigator.vibrate(pattern);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Stop any ongoing vibration
|
|
254
|
+
*/
|
|
255
|
+
function stopVibration() {
|
|
256
|
+
if (navigator.vibrate) {
|
|
257
|
+
navigator.vibrate(0);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
203
261
|
// =========================================
|
|
204
262
|
// INTERNAL PERMISSION HANDLERS
|
|
205
263
|
// =========================================
|
|
@@ -281,6 +339,42 @@ async function _requestSoundOutput() {
|
|
|
281
339
|
}
|
|
282
340
|
}
|
|
283
341
|
|
|
342
|
+
async function _requestVibrationPermission() {
|
|
343
|
+
try {
|
|
344
|
+
// Check if Vibration API is supported
|
|
345
|
+
if (!navigator.vibrate) {
|
|
346
|
+
console.warn('⚠️ Vibration API not supported on this device (likely iOS)');
|
|
347
|
+
if (_debugVisible) {
|
|
348
|
+
debugWarn('Vibration API not supported on this device');
|
|
349
|
+
}
|
|
350
|
+
window.vibrationEnabled = false;
|
|
351
|
+
_notifySketchReady();
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Test vibration with a short pulse
|
|
356
|
+
const vibrateSuccess = navigator.vibrate(1);
|
|
357
|
+
|
|
358
|
+
if (vibrateSuccess) {
|
|
359
|
+
window.vibrationEnabled = true;
|
|
360
|
+
console.log('✅ Vibration enabled');
|
|
361
|
+
} else {
|
|
362
|
+
console.warn('⚠️ Vibration API available but vibration failed');
|
|
363
|
+
window.vibrationEnabled = false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
_notifySketchReady();
|
|
367
|
+
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.error('Vibration permission error:', error);
|
|
370
|
+
if (_debugVisible) {
|
|
371
|
+
debugError('Vibration permission error:', error);
|
|
372
|
+
}
|
|
373
|
+
window.vibrationEnabled = false;
|
|
374
|
+
_notifySketchReady();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
284
378
|
function _notifySketchReady() {
|
|
285
379
|
// Call userSetupComplete if it exists
|
|
286
380
|
if (typeof userSetupComplete === 'function') {
|
|
@@ -293,6 +387,7 @@ function _notifySketchReady() {
|
|
|
293
387
|
sensors: window.sensorsEnabled,
|
|
294
388
|
microphone: window.micEnabled,
|
|
295
389
|
sound: window.soundEnabled,
|
|
390
|
+
vibration: window.vibrationEnabled,
|
|
296
391
|
gestures: window.gesturesLocked
|
|
297
392
|
}
|
|
298
393
|
}));
|
|
@@ -835,6 +930,10 @@ window.enableMicTap = enableMicTap;
|
|
|
835
930
|
window.enableMicButton = enableMicButton;
|
|
836
931
|
window.enableSoundTap = enableSoundTap;
|
|
837
932
|
window.enableSoundButton = enableSoundButton;
|
|
933
|
+
window.enableVibrationTap = enableVibrationTap;
|
|
934
|
+
window.enableVibrationButton = enableVibrationButton;
|
|
935
|
+
window.vibrate = vibrate;
|
|
936
|
+
window.stopVibration = stopVibration;
|
|
838
937
|
window.enableAllTap = enableAllTap;
|
|
839
938
|
window.enableAllButton = enableAllButton;
|
|
840
939
|
|
|
@@ -1047,6 +1146,10 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
|
|
|
1047
1146
|
p5.prototype.enableMicButton = enableMicButton;
|
|
1048
1147
|
p5.prototype.enableSoundTap = enableSoundTap;
|
|
1049
1148
|
p5.prototype.enableSoundButton = enableSoundButton;
|
|
1149
|
+
p5.prototype.enableVibrationTap = enableVibrationTap;
|
|
1150
|
+
p5.prototype.enableVibrationButton = enableVibrationButton;
|
|
1151
|
+
p5.prototype.vibrate = vibrate;
|
|
1152
|
+
p5.prototype.stopVibration = stopVibration;
|
|
1050
1153
|
p5.prototype.enableAllTap = enableAllTap;
|
|
1051
1154
|
p5.prototype.enableAllButton = enableAllButton;
|
|
1052
1155
|
|
package/dist/p5-phone.min.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.
|
|
2
|
+
* p5-phone v1.5.0
|
|
3
3
|
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
|
-
* https://github.com/
|
|
4
|
+
* https://github.com/npuckett/p5-phone
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) 2025 Nick Puckett
|
|
7
7
|
* Released under the MIT License
|
|
8
8
|
* https://opensource.org/licenses/MIT
|
|
9
9
|
*/
|
|
10
|
-
window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,window._debugErrorHandlersSet||(window._debugErrorHandlersSet=!0,window._earlyErrors=window._earlyErrors||[],window.addEventListener("error",function(e){const n=`${e.error?.message||e.message||"Unknown error"} (${e.filename?e.filename.split("/").pop():"unknown file"}:${e.lineno||"unknown line"})`;console.error("🚨 Error caught:",n),e.error?.stack&&console.error("Stack:",e.error.stack),window._earlyErrors.push({type:"error",message:"JavaScript Error: "+n,stack:e.error?.stack}),!1===window.SHOW_DEBUG||window._debugVisible||"function"==typeof showDebug&&showDebug(),window._debugVisible&&"function"==typeof debugError&&(debugError("JavaScript Error:",n),e.error?.stack&&debugError("Stack trace:",e.error.stack))}),window.addEventListener("unhandledrejection",function(e){const n=e.reason?.message||e.reason||"Unknown promise rejection";console.error("🚨 Promise rejection caught:",n),window._earlyErrors.push({type:"error",message:"Unhandled Promise Rejection: "+n}),window._debugVisible&&"function"==typeof debugError&&debugError("Unhandled Promise Rejection:",n)})),window.sensorsEnabled=!1,window.micEnabled=!1,window.soundEnabled=!1,window.gesturesLocked=!1;let _permissionsInitialized=!1,_micInstance=null;function lockGestures(){window.gesturesLocked||(console.log("🔒 Locking mobile gestures..."),_initializeGestureBlocking(),_initializeP5TouchOverrides(),window.gesturesLocked=!0,console.log("✅ Mobile gestures locked"))}function enableGyroButton(e="ENABLE MOTION SENSORS",n="Requesting motion sensors..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via button")})}function enableGyroTap(e="Tap screen to enable motion sensors"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via tap")})}function enableMicButton(e="ENABLE MICROPHONE",n="Requesting microphone access..."){_createPermissionButton(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via button")})}function enableMicTap(e="Tap screen to enable microphone"){_createTapToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via tap")})}function enableSoundButton(e="ENABLE SOUND",n="Enabling audio..."){_createPermissionButton(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via button")})}function enableSoundTap(e="Tap screen to enable sound"){_createTapToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via tap")})}async function _requestMotionPermissions(){try{if("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission){const e=await DeviceOrientationEvent.requestPermission();if(console.log("Orientation permission:",e),"undefined"!=typeof DeviceMotionEvent&&"function"==typeof DeviceMotionEvent.requestPermission){const e=await DeviceMotionEvent.requestPermission();console.log("Motion permission:",e)}}window.sensorsEnabled=!0,_notifySketchReady()}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0,_notifySketchReady()}}async function _requestMicrophonePermissions(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),"undefined"!=typeof mic&&mic&&mic.start?(mic.start(),_micInstance=mic,window.micEnabled=!0):console.warn("No microphone object found. Create one with: mic = new p5.AudioIn();"),_notifySketchReady()}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e),_notifySketchReady()}}async function _requestSoundOutput(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0,_notifySketchReady()}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0,_notifySketchReady()}}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,o){_removeExistingUI();const t=document.createElement("button");t.id="permissionButton",t.textContent=e,t.style.cssText="\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 20px 40px;\n font-size: 18px;\n font-weight: bold;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n border-radius: 12px;\n cursor: pointer;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n transition: transform 0.2s ease;\n touch-action: manipulation;\n ";const i=document.createElement("div");i.id="permissionStatus",i.textContent=n,i.style.cssText="\n position: fixed;\n top: 60%;\n left: 50%;\n transform: translate(-50%, 0);\n color: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n z-index: 999998;\n display: none;\n ",t.addEventListener("mouseenter",()=>{t.style.transform="translate(-50%, -50%) scale(1.05)"}),t.addEventListener("mouseleave",()=>{t.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{t.parentNode&&(t.style.display="none",i.style.display="block",await o(),i.style.display="none",_removeExistingUI())};t.addEventListener("click",r),t.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),t.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(t),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();const o=document.createElement("div");o.id="tapOverlay",o.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n cursor: pointer;\n touch-action: manipulation;\n ";const t=document.createElement("div");t.textContent=e,t.style.cssText="\n color: white;\n font-size: 24px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n padding: 40px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.1);\n backdrop-filter: blur(10px);\n ",o.appendChild(t);const i=async()=>{o.parentNode&&(t.textContent="Enabling...",await n(),o.parentNode&&document.body.removeChild(o))};o.addEventListener("click",i),o.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()}),o.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),i()}),document.body.appendChild(o)}function _removeExistingUI(){const e=document.getElementById("permissionButton"),n=document.getElementById("permissionStatus"),o=document.getElementById("tapOverlay");e&&e.remove(),n&&n.remove(),o&&o.remove()}function _initializeGestureBlocking(){window.history.pushState(null,"",window.location.href),window.onpopstate=function(){window.history.pushState(null,"",window.location.href)},window.addEventListener("beforeunload",function(e){e.preventDefault(),e.returnValue=""}),_initializeEdgeSwipePrevention(),_initializeOtherGesturePrevention()}function _initializeEdgeSwipePrevention(){let e=0,n=0;document.addEventListener("touchstart",function(o){o.touches&&o.touches.length>0&&(e=o.touches[0].clientX,n=o.touches[0].clientY,(e<20||e>window.innerWidth-20)&&o.preventDefault())},{passive:!1,capture:!0}),document.addEventListener("touchmove",function(o){if(!o.touches||0===o.touches.length)return;let t=o.touches[0].clientX,i=o.touches[0].clientY,r=t-e,s=i-n;(e<20&&r>0||e>window.innerWidth-20&&r<0)&&(o.preventDefault(),o.stopPropagation()),0===window.pageYOffset&&s>0&&o.preventDefault(),!o.target||"CANVAS"!==o.target.tagName||document.getElementById("tapOverlay")||document.getElementById("permissionButton")||o.preventDefault()},{passive:!1,capture:!0})}function _initializeOtherGesturePrevention(){document.addEventListener("gesturestart",function(e){e.preventDefault()}),document.addEventListener("gesturechange",function(e){e.preventDefault()}),document.addEventListener("gestureend",function(e){e.preventDefault()});let e=0;document.addEventListener("touchend",function(n){if(n.target&&("tapOverlay"===n.target.id||n.target.closest("#tapOverlay")||"permissionButton"===n.target.id||"permissionStatus"===n.target.id||n.target.closest("#permissionButton")||n.target.closest("#permissionStatus")))return;const o=Date.now();o-e<=300&&n.preventDefault(),e=o},!1),window.oncontextmenu=function(e){return e.preventDefault(),e.stopPropagation(),!1}}function _initializeP5TouchOverrides(){setTimeout(()=>{if(window._setupDone)_overrideP5Touch();else{const e=setInterval(()=>{window._setupDone&&(_overrideP5Touch(),clearInterval(e))},100)}},100)}function _overrideP5Touch(){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},o=window.touchEnded||function(){},t=window.mousePressed||function(){},i=window.mouseDragged||function(){},r=window.mouseReleased||function(){};window.touchStarted=function(n){return e(n),!1},window.touchMoved=function(e){return n(e),!1},window.touchEnded=function(e){return o(e),!1},window.mousePressed=function(e){return t(e),!1},window.mouseDragged=function(e){return i(e),!1},window.mouseReleased=function(e){return r(e),!1}}document.addEventListener("DOMContentLoaded",function(){const e=document.getElementById("startButton"),n=document.getElementById("statusText");e&&n&&(console.warn("⚠️ Legacy HTML elements detected. Consider using the new API functions instead."),e.addEventListener("click",async()=>{e.classList.add("hidden"),n.classList.remove("hidden"),n.textContent="Requesting permissions...",await _requestMotionPermissions(),await _requestMicrophonePermissions(),n.classList.add("hidden")}),lockGestures())});let _debugPanel=null,_debugVisible=!1,_debugMessages=[];const MAX_DEBUG_MESSAGES=20;function showDebug(){_createDebugPanel(),_debugPanel.style.display="block",_debugVisible=!0,window._debugVisible=!0,_setupConsoleOverrides(),_displayEarlyErrors()}function hideDebug(){_debugPanel&&(_debugPanel.style.display="none",_debugVisible=!1)}function toggleDebug(){_debugVisible?hideDebug():showDebug()}function debug(...e){console.log(...e);_addDebugMessage(e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" "),"log")}function debugError(...e){(window._originalConsoleError||console.error).apply(console,e);_addDebugMessage(`❌ ERROR: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"error")}function debugWarn(...e){(window._originalConsoleWarn||console.warn).apply(console,e);_addDebugMessage(`⚠️ WARNING: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"warning")}function _addDebugMessage(e,n="log"){const o={text:`[${(new Date).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}] ${e}`,type:n};_debugMessages.push(o),_debugMessages.length>20&&_debugMessages.shift(),_debugPanel&&_updateDebugDisplay()}function _setupConsoleOverrides(){window._consoleOverrideSet||(window._consoleOverrideSet=!0,window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,console.error=function(...e){window._originalConsoleError.apply(console,e),_debugVisible&&debugError(...e)},console.warn=function(...e){window._originalConsoleWarn.apply(console,e),_debugVisible&&debugWarn(...e)})}function _displayEarlyErrors(){window._earlyErrors&&window._earlyErrors.length>0&&(debugError(`🚨 Found ${window._earlyErrors.length} early error(s):`),window._earlyErrors.forEach(e=>{debugError(e.message),e.stack&&debugError("Stack trace:",e.stack)}),window._earlyErrors=[])}function _createDebugPanel(){if(_debugPanel)return;_debugPanel=document.createElement("div"),_debugPanel.id="mobile-debug-panel",_debugPanel.innerHTML='\n <div id="mobile-debug-header">\n <span>Debug</span>\n <button id="mobile-debug-close">×</button>\n </div>\n <div id="mobile-debug-content"></div>\n ';const e=document.createElement("style");e.textContent="\n #mobile-debug-panel {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 350px;\n max-width: calc(100vw - 40px);\n max-height: 400px;\n background: rgba(0, 0, 0, 0.9);\n color: #ffffff;\n font-family: 'Courier New', monospace;\n font-size: 12px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 10000;\n display: none;\n }\n \n #mobile-debug-header {\n background: rgba(255, 255, 255, 0.1);\n padding: 8px 12px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-radius: 8px 8px 0 0;\n }\n \n #mobile-debug-header span {\n font-weight: bold;\n font-size: 13px;\n }\n \n #mobile-debug-close {\n background: none;\n border: none;\n color: #ffffff;\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n #mobile-debug-close:hover {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n }\n \n #mobile-debug-content {\n padding: 12px;\n max-height: 340px;\n overflow-y: auto;\n word-wrap: break-word;\n line-height: 1.4;\n }\n \n .debug-message {\n margin-bottom: 4px;\n white-space: pre-wrap;\n }\n \n .debug-message.error {\n color: #ff6b6b;\n background: rgba(255, 107, 107, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ff6b6b;\n }\n \n .debug-message.warning {\n color: #ffd93d;\n background: rgba(255, 217, 61, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ffd93d;\n }\n \n .debug-timestamp {\n color: #888;\n font-size: 10px;\n }\n \n @media (max-width: 480px) {\n #mobile-debug-panel {\n width: calc(100vw - 20px);\n right: 10px;\n top: 10px;\n }\n }\n ",document.head.appendChild(e),document.body.appendChild(_debugPanel),document.getElementById("mobile-debug-close").onclick=hideDebug,_updateDebugDisplay()}function _updateDebugDisplay(){if(!_debugPanel)return;const e=document.getElementById("mobile-debug-content");e&&(e.innerHTML=_debugMessages.map(e=>"string"==typeof e?`<div class="debug-message">${e}</div>`:`<div class="debug-message ${e.type}">${e.text}</div>`).join(""),e.scrollTop=e.scrollHeight)}debug.clear=function(){_debugMessages=[],_debugPanel&&_updateDebugDisplay(),console.clear()},window.debug=debug,window.debugError=debugError,window.debugWarn=debugWarn,window.showDebug=showDebug,window.hideDebug=hideDebug,window.toggleDebug=toggleDebug,window.lockGestures=lockGestures,window.enableGyroTap=enableGyroTap,window.enableGyroButton=enableGyroButton,window.enableMicTap=enableMicTap,window.enableMicButton=enableMicButton,window.enableSoundTap=enableSoundTap,window.enableSoundButton=enableSoundButton,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton,"undefined"!=typeof p5&&p5.prototype&&(p5.prototype.lockGestures=lockGestures,p5.prototype.enableGyroTap=enableGyroTap,p5.prototype.enableGyroButton=enableGyroButton,p5.prototype.enableMicTap=enableMicTap,p5.prototype.enableMicButton=enableMicButton,p5.prototype.enableSoundTap=enableSoundTap,p5.prototype.enableSoundButton=enableSoundButton,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,p5.prototype.showDebug=showDebug,p5.prototype.hideDebug=hideDebug,p5.prototype.toggleDebug=toggleDebug,p5.prototype.debug=debug,p5.prototype.debugError=debugError,p5.prototype.debugWarn=debugWarn,console.log("✅ Mobile p5.js Permissions: p5.prototype functions registered"));
|
|
10
|
+
window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,window._debugErrorHandlersSet||(window._debugErrorHandlersSet=!0,window._earlyErrors=window._earlyErrors||[],window.addEventListener("error",function(e){const n=`${e.error?.message||e.message||"Unknown error"} (${e.filename?e.filename.split("/").pop():"unknown file"}:${e.lineno||"unknown line"})`;console.error("🚨 Error caught:",n),e.error?.stack&&console.error("Stack:",e.error.stack),window._earlyErrors.push({type:"error",message:"JavaScript Error: "+n,stack:e.error?.stack}),!1===window.SHOW_DEBUG||window._debugVisible||"function"==typeof showDebug&&showDebug(),window._debugVisible&&"function"==typeof debugError&&(debugError("JavaScript Error:",n),e.error?.stack&&debugError("Stack trace:",e.error.stack))}),window.addEventListener("unhandledrejection",function(e){const n=e.reason?.message||e.reason||"Unknown promise rejection";console.error("🚨 Promise rejection caught:",n),window._earlyErrors.push({type:"error",message:"Unhandled Promise Rejection: "+n}),window._debugVisible&&"function"==typeof debugError&&debugError("Unhandled Promise Rejection:",n)})),window.sensorsEnabled=!1,window.micEnabled=!1,window.soundEnabled=!1,window.gesturesLocked=!1,window.vibrationEnabled=!1;let _permissionsInitialized=!1,_micInstance=null;function lockGestures(){window.gesturesLocked||(console.log("🔒 Locking mobile gestures..."),_initializeGestureBlocking(),_initializeP5TouchOverrides(),window.gesturesLocked=!0,console.log("✅ Mobile gestures locked"))}function enableGyroButton(e="ENABLE MOTION SENSORS",n="Requesting motion sensors..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via button")})}function enableGyroTap(e="Tap screen to enable motion sensors"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via tap")})}function enableMicButton(e="ENABLE MICROPHONE",n="Requesting microphone access..."){_createPermissionButton(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via button")})}function enableMicTap(e="Tap screen to enable microphone"){_createTapToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via tap")})}function enableSoundButton(e="ENABLE SOUND",n="Enabling audio..."){_createPermissionButton(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via button")})}function enableSoundTap(e="Tap screen to enable sound"){_createTapToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via tap")})}function enableVibrationButton(e="ENABLE VIBRATION",n="Enabling vibration..."){_createPermissionButton(e,n,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via button")})}function enableVibrationTap(e="Tap screen to enable vibration"){_createTapToEnable(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via tap")})}function vibrate(e){return window.vibrationEnabled?navigator.vibrate?navigator.vibrate(e):(console.warn("⚠️ Vibration API not supported on this device"),!1):(console.warn("⚠️ Vibration not enabled. Call enableVibrationTap() or enableVibrationButton() first."),!1)}function stopVibration(){navigator.vibrate&&navigator.vibrate(0)}async function _requestMotionPermissions(){try{if("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission){const e=await DeviceOrientationEvent.requestPermission();if(console.log("Orientation permission:",e),"undefined"!=typeof DeviceMotionEvent&&"function"==typeof DeviceMotionEvent.requestPermission){const e=await DeviceMotionEvent.requestPermission();console.log("Motion permission:",e)}}window.sensorsEnabled=!0,_notifySketchReady()}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0,_notifySketchReady()}}async function _requestMicrophonePermissions(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),"undefined"!=typeof mic&&mic&&mic.start?(mic.start(),_micInstance=mic,window.micEnabled=!0):console.warn("No microphone object found. Create one with: mic = new p5.AudioIn();"),_notifySketchReady()}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e),_notifySketchReady()}}async function _requestSoundOutput(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0,_notifySketchReady()}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0,_notifySketchReady()}}async function _requestVibrationPermission(){try{if(!navigator.vibrate)return console.warn("⚠️ Vibration API not supported on this device (likely iOS)"),_debugVisible&&debugWarn("Vibration API not supported on this device"),window.vibrationEnabled=!1,void _notifySketchReady();navigator.vibrate(1)?(window.vibrationEnabled=!0,console.log("✅ Vibration enabled")):(console.warn("⚠️ Vibration API available but vibration failed"),window.vibrationEnabled=!1),_notifySketchReady()}catch(e){console.error("Vibration permission error:",e),_debugVisible&&debugError("Vibration permission error:",e),window.vibrationEnabled=!1,_notifySketchReady()}}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,vibration:window.vibrationEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,o){_removeExistingUI();const t=document.createElement("button");t.id="permissionButton",t.textContent=e,t.style.cssText="\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 20px 40px;\n font-size: 18px;\n font-weight: bold;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n border-radius: 12px;\n cursor: pointer;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n transition: transform 0.2s ease;\n touch-action: manipulation;\n ";const i=document.createElement("div");i.id="permissionStatus",i.textContent=n,i.style.cssText="\n position: fixed;\n top: 60%;\n left: 50%;\n transform: translate(-50%, 0);\n color: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n z-index: 999998;\n display: none;\n ",t.addEventListener("mouseenter",()=>{t.style.transform="translate(-50%, -50%) scale(1.05)"}),t.addEventListener("mouseleave",()=>{t.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{t.parentNode&&(t.style.display="none",i.style.display="block",await o(),i.style.display="none",_removeExistingUI())};t.addEventListener("click",r),t.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),t.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(t),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();const o=document.createElement("div");o.id="tapOverlay",o.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n cursor: pointer;\n touch-action: manipulation;\n ";const t=document.createElement("div");t.textContent=e,t.style.cssText="\n color: white;\n font-size: 24px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n padding: 40px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.1);\n backdrop-filter: blur(10px);\n ",o.appendChild(t);const i=async()=>{o.parentNode&&(t.textContent="Enabling...",await n(),o.parentNode&&document.body.removeChild(o))};o.addEventListener("click",i),o.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()}),o.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),i()}),document.body.appendChild(o)}function _removeExistingUI(){const e=document.getElementById("permissionButton"),n=document.getElementById("permissionStatus"),o=document.getElementById("tapOverlay");e&&e.remove(),n&&n.remove(),o&&o.remove()}function _initializeGestureBlocking(){window.history.pushState(null,"",window.location.href),window.onpopstate=function(){window.history.pushState(null,"",window.location.href)},window.addEventListener("beforeunload",function(e){e.preventDefault(),e.returnValue=""}),_initializeEdgeSwipePrevention(),_initializeOtherGesturePrevention()}function _initializeEdgeSwipePrevention(){let e=0,n=0;document.addEventListener("touchstart",function(o){o.touches&&o.touches.length>0&&(e=o.touches[0].clientX,n=o.touches[0].clientY,(e<20||e>window.innerWidth-20)&&o.preventDefault())},{passive:!1,capture:!0}),document.addEventListener("touchmove",function(o){if(!o.touches||0===o.touches.length)return;let t=o.touches[0].clientX,i=o.touches[0].clientY,r=t-e,a=i-n;(e<20&&r>0||e>window.innerWidth-20&&r<0)&&(o.preventDefault(),o.stopPropagation()),0===window.pageYOffset&&a>0&&o.preventDefault(),!o.target||"CANVAS"!==o.target.tagName||document.getElementById("tapOverlay")||document.getElementById("permissionButton")||o.preventDefault()},{passive:!1,capture:!0})}function _initializeOtherGesturePrevention(){document.addEventListener("gesturestart",function(e){e.preventDefault()}),document.addEventListener("gesturechange",function(e){e.preventDefault()}),document.addEventListener("gestureend",function(e){e.preventDefault()});let e=0;document.addEventListener("touchend",function(n){if(n.target&&("tapOverlay"===n.target.id||n.target.closest("#tapOverlay")||"permissionButton"===n.target.id||"permissionStatus"===n.target.id||n.target.closest("#permissionButton")||n.target.closest("#permissionStatus")))return;const o=Date.now();o-e<=300&&n.preventDefault(),e=o},!1),window.oncontextmenu=function(e){return e.preventDefault(),e.stopPropagation(),!1}}function _initializeP5TouchOverrides(){setTimeout(()=>{if(window._setupDone)_overrideP5Touch();else{const e=setInterval(()=>{window._setupDone&&(_overrideP5Touch(),clearInterval(e))},100)}},100)}function _overrideP5Touch(){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},o=window.touchEnded||function(){},t=window.mousePressed||function(){},i=window.mouseDragged||function(){},r=window.mouseReleased||function(){};window.touchStarted=function(n){return e(n),!1},window.touchMoved=function(e){return n(e),!1},window.touchEnded=function(e){return o(e),!1},window.mousePressed=function(e){return t(e),!1},window.mouseDragged=function(e){return i(e),!1},window.mouseReleased=function(e){return r(e),!1}}document.addEventListener("DOMContentLoaded",function(){const e=document.getElementById("startButton"),n=document.getElementById("statusText");e&&n&&(console.warn("⚠️ Legacy HTML elements detected. Consider using the new API functions instead."),e.addEventListener("click",async()=>{e.classList.add("hidden"),n.classList.remove("hidden"),n.textContent="Requesting permissions...",await _requestMotionPermissions(),await _requestMicrophonePermissions(),n.classList.add("hidden")}),lockGestures())});let _debugPanel=null,_debugVisible=!1,_debugMessages=[];const MAX_DEBUG_MESSAGES=20;function showDebug(){_createDebugPanel(),_debugPanel.style.display="block",_debugVisible=!0,window._debugVisible=!0,_setupConsoleOverrides(),_displayEarlyErrors()}function hideDebug(){_debugPanel&&(_debugPanel.style.display="none",_debugVisible=!1)}function toggleDebug(){_debugVisible?hideDebug():showDebug()}function debug(...e){console.log(...e);_addDebugMessage(e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" "),"log")}function debugError(...e){(window._originalConsoleError||console.error).apply(console,e);_addDebugMessage(`❌ ERROR: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"error")}function debugWarn(...e){(window._originalConsoleWarn||console.warn).apply(console,e);_addDebugMessage(`⚠️ WARNING: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"warning")}function _addDebugMessage(e,n="log"){const o={text:`[${(new Date).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}] ${e}`,type:n};_debugMessages.push(o),_debugMessages.length>20&&_debugMessages.shift(),_debugPanel&&_updateDebugDisplay()}function _setupConsoleOverrides(){window._consoleOverrideSet||(window._consoleOverrideSet=!0,window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,console.error=function(...e){window._originalConsoleError.apply(console,e),_debugVisible&&debugError(...e)},console.warn=function(...e){window._originalConsoleWarn.apply(console,e),_debugVisible&&debugWarn(...e)})}function _displayEarlyErrors(){window._earlyErrors&&window._earlyErrors.length>0&&(debugError(`🚨 Found ${window._earlyErrors.length} early error(s):`),window._earlyErrors.forEach(e=>{debugError(e.message),e.stack&&debugError("Stack trace:",e.stack)}),window._earlyErrors=[])}function _createDebugPanel(){if(_debugPanel)return;_debugPanel=document.createElement("div"),_debugPanel.id="mobile-debug-panel",_debugPanel.innerHTML='\n <div id="mobile-debug-header">\n <span>Debug</span>\n <button id="mobile-debug-close">×</button>\n </div>\n <div id="mobile-debug-content"></div>\n ';const e=document.createElement("style");e.textContent="\n #mobile-debug-panel {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 350px;\n max-width: calc(100vw - 40px);\n max-height: 400px;\n background: rgba(0, 0, 0, 0.9);\n color: #ffffff;\n font-family: 'Courier New', monospace;\n font-size: 12px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 10000;\n display: none;\n }\n \n #mobile-debug-header {\n background: rgba(255, 255, 255, 0.1);\n padding: 8px 12px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-radius: 8px 8px 0 0;\n }\n \n #mobile-debug-header span {\n font-weight: bold;\n font-size: 13px;\n }\n \n #mobile-debug-close {\n background: none;\n border: none;\n color: #ffffff;\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n #mobile-debug-close:hover {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n }\n \n #mobile-debug-content {\n padding: 12px;\n max-height: 340px;\n overflow-y: auto;\n word-wrap: break-word;\n line-height: 1.4;\n }\n \n .debug-message {\n margin-bottom: 4px;\n white-space: pre-wrap;\n }\n \n .debug-message.error {\n color: #ff6b6b;\n background: rgba(255, 107, 107, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ff6b6b;\n }\n \n .debug-message.warning {\n color: #ffd93d;\n background: rgba(255, 217, 61, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ffd93d;\n }\n \n .debug-timestamp {\n color: #888;\n font-size: 10px;\n }\n \n @media (max-width: 480px) {\n #mobile-debug-panel {\n width: calc(100vw - 20px);\n right: 10px;\n top: 10px;\n }\n }\n ",document.head.appendChild(e),document.body.appendChild(_debugPanel),document.getElementById("mobile-debug-close").onclick=hideDebug,_updateDebugDisplay()}function _updateDebugDisplay(){if(!_debugPanel)return;const e=document.getElementById("mobile-debug-content");e&&(e.innerHTML=_debugMessages.map(e=>"string"==typeof e?`<div class="debug-message">${e}</div>`:`<div class="debug-message ${e.type}">${e.text}</div>`).join(""),e.scrollTop=e.scrollHeight)}debug.clear=function(){_debugMessages=[],_debugPanel&&_updateDebugDisplay(),console.clear()},window.debug=debug,window.debugError=debugError,window.debugWarn=debugWarn,window.showDebug=showDebug,window.hideDebug=hideDebug,window.toggleDebug=toggleDebug,window.lockGestures=lockGestures,window.enableGyroTap=enableGyroTap,window.enableGyroButton=enableGyroButton,window.enableMicTap=enableMicTap,window.enableMicButton=enableMicButton,window.enableSoundTap=enableSoundTap,window.enableSoundButton=enableSoundButton,window.enableVibrationTap=enableVibrationTap,window.enableVibrationButton=enableVibrationButton,window.vibrate=vibrate,window.stopVibration=stopVibration,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton,"undefined"!=typeof p5&&p5.prototype&&(p5.prototype.lockGestures=lockGestures,p5.prototype.enableGyroTap=enableGyroTap,p5.prototype.enableGyroButton=enableGyroButton,p5.prototype.enableMicTap=enableMicTap,p5.prototype.enableMicButton=enableMicButton,p5.prototype.enableSoundTap=enableSoundTap,p5.prototype.enableSoundButton=enableSoundButton,p5.prototype.enableVibrationTap=enableVibrationTap,p5.prototype.enableVibrationButton=enableVibrationButton,p5.prototype.vibrate=vibrate,p5.prototype.stopVibration=stopVibration,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,p5.prototype.showDebug=showDebug,p5.prototype.hideDebug=hideDebug,p5.prototype.toggleDebug=toggleDebug,p5.prototype.debug=debug,p5.prototype.debugError=debugError,p5.prototype.debugWarn=debugWarn,console.log("✅ Mobile p5.js Permissions: p5.prototype functions registered"));
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title>P5.js Mobile - Microphone Level</title>
|
|
5
|
-
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.
|
|
5
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.10/lib/p5.min.js"></script>
|
|
6
6
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.0/addons/p5.sound.min.js"></script>
|
|
7
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
8
8
|
<style>
|
|
9
9
|
body {
|
|
10
10
|
margin: 0;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/addons/p5.sound.min.js"></script>
|
|
22
22
|
|
|
23
23
|
<!-- Load the mobile p5.js permissions library -->
|
|
24
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
24
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
25
25
|
|
|
26
26
|
</head>
|
|
27
27
|
<body>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/addons/p5.sound.min.js"></script>
|
|
22
22
|
|
|
23
23
|
<!-- Load the mobile p5.js permissions library -->
|
|
24
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
24
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
25
25
|
|
|
26
26
|
</head>
|
|
27
27
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Haptic Feedback Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates how to use the vibration motor on Android devices to provide haptic feedback for different touch interactions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Four Touch Zones**: Screen divided into 2x2 grid with different vibration patterns
|
|
8
|
+
- **Quick Tap**: 20ms single pulse for subtle feedback
|
|
9
|
+
- **Double Tap**: Two 50ms pulses with 30ms pause between
|
|
10
|
+
- **Triple Tap**: Three 50ms pulses for more prominent feedback
|
|
11
|
+
- **Long Press**: 200ms extended pulse for significant actions
|
|
12
|
+
|
|
13
|
+
## Android Only
|
|
14
|
+
|
|
15
|
+
⚠️ **Important**: Vibration is only supported on Android devices. iOS does not support the Vibration API, so this example will not provide haptic feedback on iPhones or iPads.
|
|
16
|
+
|
|
17
|
+
## How It Works
|
|
18
|
+
|
|
19
|
+
1. Tap screen to enable vibration (browser security requirement)
|
|
20
|
+
2. Touch different zones to experience different vibration patterns
|
|
21
|
+
3. Each zone has a distinct pattern:
|
|
22
|
+
- Single number = duration in milliseconds
|
|
23
|
+
- Array = pattern of [vibrate, pause, vibrate, pause, ...]
|
|
24
|
+
|
|
25
|
+
## Key Concepts
|
|
26
|
+
|
|
27
|
+
- `enableVibrationTap()` - Request permission to use vibration
|
|
28
|
+
- `window.vibrationEnabled` - Check if vibration is available
|
|
29
|
+
- `vibrate(pattern)` - Trigger vibration with duration or pattern
|
|
30
|
+
- Combining visual and haptic feedback for enhanced UX
|
|
31
|
+
|
|
32
|
+
## Code Highlights
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
// Enable vibration with tap
|
|
36
|
+
enableVibrationTap('Tap to enable vibration');
|
|
37
|
+
|
|
38
|
+
// Single vibration
|
|
39
|
+
vibrate(50); // 50ms pulse
|
|
40
|
+
|
|
41
|
+
// Pattern vibration
|
|
42
|
+
vibrate([100, 50, 100]); // vibrate-pause-vibrate
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Use Cases
|
|
46
|
+
|
|
47
|
+
- Button press feedback
|
|
48
|
+
- Game interactions
|
|
49
|
+
- Alert notifications
|
|
50
|
+
- Touch zone confirmation
|
|
51
|
+
- Gesture completion feedback
|
|
@@ -0,0 +1,28 @@
|
|
|
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>Haptic Feedback - Mobile p5.js</title>
|
|
7
|
+
|
|
8
|
+
<!-- Basic CSS to remove browser defaults and align canvas -->
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
16
|
+
|
|
17
|
+
<!-- Load p5.js library -->
|
|
18
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
|
+
|
|
20
|
+
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.4.4/dist/p5-phone.min.js"></script>
|
|
22
|
+
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<!-- Load the p5.js sketch -->
|
|
26
|
+
<script src="sketch.js"></script>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|