p5-phone 1.9.1 → 1.9.2
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 +4 -4
- package/dist/p5-phone.js +78 -71
- package/dist/p5-phone.min.js +2 -2
- package/examples/Phone Sensor Examples/microphone/01_mic_level/index.html +3 -2
- package/examples/Phone Sensor Examples/microphone/02_speech_recognition/index.html +3 -2
- package/examples/Phone Sensor Examples/movement/01_orientation_basic/index.html +2 -2
- package/examples/Phone Sensor Examples/movement/02_rotational_velocity/index.html +2 -2
- package/examples/Phone Sensor Examples/movement/03_acceleration/index.html +2 -2
- package/examples/Phone Sensor Examples/movement/04_device_shaken/index.html +2 -2
- package/examples/Phone Sensor Examples/movement/05_device_moved/index.html +2 -2
- package/examples/Phone Sensor Examples/movement/06_device_orientation/index.html +2 -2
- package/examples/Phone Sensor Examples/nfc/01_nfc_read/index.html +3 -3
- package/examples/Phone Sensor Examples/nfc/02_two_tag_effects/index.html +3 -3
- package/examples/Phone Sensor Examples/sound/01_dual_audio/index.html +3 -3
- package/examples/Phone Sensor Examples/sound/01_dual_audio/sketch.js +5 -18
- package/examples/Phone Sensor Examples/sound/02_volume_touches/index.html +3 -3
- package/examples/Phone Sensor Examples/sound/02_volume_touches/sketch.js +3 -9
- package/examples/Phone Sensor Examples/sound/03_motion_synth/index.html +3 -3
- package/examples/Phone Sensor Examples/touch/01_touch_basic/index.html +2 -2
- package/examples/Phone Sensor Examples/touch/02_touch_zones/index.html +2 -2
- package/examples/Phone Sensor Examples/touch/03_touch_count/index.html +2 -2
- package/examples/Phone Sensor Examples/touch/04_touch_distance/index.html +2 -2
- package/examples/Phone Sensor Examples/touch/05_touch_angle/index.html +2 -2
- package/examples/Phone Sensor Examples/vibration/01_haptic_feedback/index.html +2 -2
- package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/index.html +3 -2
- package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/movement/04_device_shaken/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/movement/05_device_moved/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/movement/06_device_orientation/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/index.html +3 -2
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/sketch.js +20 -23
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/index.html +3 -2
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/sketch.js +27 -30
- package/examples/Phone Sensor Examples - Minimal/sound/03_motion_synth/index.html +3 -2
- package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/index.html +2 -1
- package/examples/Phone Sensor Examples - Minimal/vibration/01_haptic_feedback/index.html +2 -1
- package/examples/Phone and Gif/collision/index.html +2 -1
- package/examples/Phone and Gif/collision/sketch.js +28 -31
- package/examples/Phone and Gif/fetch/index.html +2 -1
- package/examples/Phone and Gif/fetch/sketch.js +27 -30
- package/examples/Phone and Gif/fly/index.html +2 -1
- package/examples/Phone and Gif/fly/sketch.js +19 -22
- package/examples/Phone and Gif/roll/index.html +2 -1
- package/examples/Phone and Gif/roll/sketch.js +21 -24
- package/examples/UIStyles/banner-style/index.html +3 -3
- package/examples/UIStyles/canvas-style/index.html +3 -3
- package/examples/UIStyles/custom-element/index.html +3 -3
- package/examples/UXcompare/button-vs-movement/index.html +2 -2
- package/examples/UXcompare/button-vs-orientation/index.html +2 -2
- package/examples/UXcompare/button-vs-shake/index.html +2 -2
- package/examples/UXcompare/gyroscope-demo/index.html +2 -2
- package/examples/UXcompare/microphone-demo/index.html +3 -3
- package/examples/UXcompare/slider-vs-angle/index.html +2 -2
- package/examples/UXcompare/slider-vs-distance/index.html +2 -2
- package/examples/UXcompare/slider-vs-microphone/index.html +3 -3
- package/examples/UXcompare/slider-vs-touches/index.html +2 -2
- package/examples/UXcompare/sliders-vs-acceleration/index.html +2 -2
- package/examples/UXcompare/sliders-vs-rotation/index.html +2 -2
- package/examples/blankTemplate/index.html +3 -3
- package/examples/homepage/index.html +244 -1515
- package/examples/{homepage-v2 → homepage}/scripts/api-data.js +32 -0
- package/examples/{homepage-v2 → homepage}/scripts/examples-data.js +16 -16
- package/examples/{homepage-v2 → homepage}/scripts/render.js +61 -11
- package/examples/{homepage-v2 → homepage}/styles/main.css +26 -12
- package/examples/homepage-v1/index.html +1538 -0
- package/examples/homepage-v2/index.html +5 -255
- package/examples/ml5/Gaze_detector_class/index.html +2 -2
- package/examples/ml5/PHONE_BodyPose_two_points/index.html +2 -2
- package/examples/ml5/PHONE_FaceMesh_two_points/index.html +2 -2
- package/examples/ml5/PHONE_HandPose_two_points/index.html +2 -2
- package/examples/ml5/PHONE_ObjectDetection/index.html +3 -3
- package/package.json +2 -2
- package/src/p5-phone.js +78 -71
- /package/examples/{homepage-v2 → homepage}/scripts/navigation.js +0 -0
package/README.md
CHANGED
|
@@ -130,8 +130,8 @@ p5-phone automatically detects the p5.js version and adjusts its internal touch
|
|
|
130
130
|
</style>
|
|
131
131
|
|
|
132
132
|
<!-- Load p5.js library -->
|
|
133
|
-
<script src="https://
|
|
134
|
-
|
|
133
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
134
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
135
135
|
|
|
136
136
|
<!-- Load p5-phone library -->
|
|
137
137
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.min.js"></script>
|
|
@@ -433,7 +433,7 @@ function draw() {
|
|
|
433
433
|
|
|
434
434
|
**Important:** Microphone examples require the p5.sound library. Add this script tag to your HTML:
|
|
435
435
|
```html
|
|
436
|
-
<script src="https://
|
|
436
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.sound@0.3.0/dist/p5.sound.min.js"></script>
|
|
437
437
|
```
|
|
438
438
|
|
|
439
439
|
**Commands:**
|
|
@@ -489,7 +489,7 @@ function draw() {
|
|
|
489
489
|
|
|
490
490
|
**Important:** Sound examples require the p5.sound library. Add this script tag to your HTML:
|
|
491
491
|
```html
|
|
492
|
-
<script src="https://
|
|
492
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.sound@0.3.0/dist/p5.sound.min.js"></script>
|
|
493
493
|
```
|
|
494
494
|
|
|
495
495
|
**Commands:**
|
package/dist/p5-phone.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.
|
|
2
|
+
* p5-phone v1.9.2
|
|
3
3
|
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
4
|
* https://github.com/npuckett/p5-phone
|
|
5
5
|
*
|
|
@@ -2525,10 +2525,11 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
|
|
|
2525
2525
|
// =========================================
|
|
2526
2526
|
|
|
2527
2527
|
/**
|
|
2528
|
-
* Add functions to p5.js prototype for namespace support
|
|
2529
|
-
*
|
|
2528
|
+
* Add functions to p5.js prototype for namespace support in p5.js 1.x.
|
|
2529
|
+
* p5.js 2.x uses p5.registerAddon() below; registering in both places
|
|
2530
|
+
* creates duplicate globals during p5 2 global-mode binding.
|
|
2530
2531
|
*/
|
|
2531
|
-
if (typeof p5 !== 'undefined' && p5.prototype) {
|
|
2532
|
+
if (typeof p5 !== 'undefined' && p5.prototype && typeof p5.registerAddon !== 'function') {
|
|
2532
2533
|
// Core permission functions
|
|
2533
2534
|
p5.prototype.lockGestures = lockGestures;
|
|
2534
2535
|
p5.prototype.enableGyroTap = enableGyroTap;
|
|
@@ -2609,73 +2610,79 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
|
|
|
2609
2610
|
*/
|
|
2610
2611
|
if (typeof p5 !== 'undefined' && typeof p5.registerAddon === 'function') {
|
|
2611
2612
|
p5.registerAddon(function(p5, fn, lifecycles) {
|
|
2612
|
-
//
|
|
2613
|
-
//
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2613
|
+
// In p5.js 2 global mode, p5 binds every prototype property onto window.
|
|
2614
|
+
// p5-phone's top-level function declarations already create globals such
|
|
2615
|
+
// as lockGestures, so adding those names to p5.prototype before binding
|
|
2616
|
+
// causes "Cannot redefine property" errors. Attach instance methods after
|
|
2617
|
+
// global binding but before setup() instead.
|
|
2618
|
+
lifecycles.presetup = function() {
|
|
2619
|
+
// Core permission functions
|
|
2620
|
+
this.lockGestures = lockGestures;
|
|
2621
|
+
this.enableGyroTap = enableGyroTap;
|
|
2622
|
+
this.enableGyroButton = enableGyroButton;
|
|
2623
|
+
this.enableMicTap = enableMicTap;
|
|
2624
|
+
this.enableMicButton = enableMicButton;
|
|
2625
|
+
this.enableSoundTap = enableSoundTap;
|
|
2626
|
+
this.enableSoundButton = enableSoundButton;
|
|
2627
|
+
this.enableSpeechTap = enableSpeechTap;
|
|
2628
|
+
this.enableSpeechButton = enableSpeechButton;
|
|
2629
|
+
this.enableVibrationTap = enableVibrationTap;
|
|
2630
|
+
this.enableVibrationButton = enableVibrationButton;
|
|
2631
|
+
this.vibrate = vibrate;
|
|
2632
|
+
this.stopVibration = stopVibration;
|
|
2633
|
+
this.enableNfcTap = enableNfcTap;
|
|
2634
|
+
this.enableNfcButton = enableNfcButton;
|
|
2635
|
+
this.stopNfc = stopNfc;
|
|
2636
|
+
this.setNfcTagAlias = setNfcTagAlias;
|
|
2637
|
+
this.getNfcTagAlias = getNfcTagAlias;
|
|
2638
|
+
this.isNfcTag = isNfcTag;
|
|
2639
|
+
this.enableAllTap = enableAllTap;
|
|
2640
|
+
this.enableAllButton = enableAllButton;
|
|
2641
|
+
|
|
2642
|
+
// Canvas-first-touch style
|
|
2643
|
+
this.enableGyroCanvas = enableGyroCanvas;
|
|
2644
|
+
this.enableMicCanvas = enableMicCanvas;
|
|
2645
|
+
this.enableSoundCanvas = enableSoundCanvas;
|
|
2646
|
+
this.enableSpeechCanvas = enableSpeechCanvas;
|
|
2647
|
+
this.enableVibrationCanvas = enableVibrationCanvas;
|
|
2648
|
+
this.enableNfcCanvas = enableNfcCanvas;
|
|
2649
|
+
this.enableAllCanvas = enableAllCanvas;
|
|
2650
|
+
this.enableCameraCanvas = enableCameraCanvas;
|
|
2651
|
+
|
|
2652
|
+
// Banner style
|
|
2653
|
+
this.enableGyroBanner = enableGyroBanner;
|
|
2654
|
+
this.enableMicBanner = enableMicBanner;
|
|
2655
|
+
this.enableSoundBanner = enableSoundBanner;
|
|
2656
|
+
this.enableSpeechBanner = enableSpeechBanner;
|
|
2657
|
+
this.enableVibrationBanner = enableVibrationBanner;
|
|
2658
|
+
this.enableNfcBanner = enableNfcBanner;
|
|
2659
|
+
this.enableAllBanner = enableAllBanner;
|
|
2660
|
+
this.enableCameraBanner = enableCameraBanner;
|
|
2661
|
+
|
|
2662
|
+
// Custom element binding
|
|
2663
|
+
this.enableGyroOn = enableGyroOn;
|
|
2664
|
+
this.enableMicOn = enableMicOn;
|
|
2665
|
+
this.enableSoundOn = enableSoundOn;
|
|
2666
|
+
this.enableSpeechOn = enableSpeechOn;
|
|
2667
|
+
this.enableVibrationOn = enableVibrationOn;
|
|
2668
|
+
this.enableNfcOn = enableNfcOn;
|
|
2669
|
+
this.enableAllOn = enableAllOn;
|
|
2670
|
+
this.enableCameraOn = enableCameraOn;
|
|
2671
|
+
|
|
2672
|
+
// Camera functions
|
|
2673
|
+
this.createPhoneCamera = createPhoneCamera;
|
|
2674
|
+
this.enableCameraButton = enableCameraButton;
|
|
2675
|
+
this.enableCameraTap = enableCameraTap;
|
|
2676
|
+
|
|
2677
|
+
// Debug functions
|
|
2678
|
+
this.showDebug = showDebug;
|
|
2679
|
+
this.hideDebug = hideDebug;
|
|
2680
|
+
this.toggleDebug = toggleDebug;
|
|
2681
|
+
this.debug = debug;
|
|
2682
|
+
this.debugError = debugError;
|
|
2683
|
+
this.debugWarn = debugWarn;
|
|
2684
|
+
};
|
|
2685
|
+
|
|
2679
2686
|
console.log('✅ Mobile p5.js Permissions: registered as p5.js 2.0 addon');
|
|
2680
2687
|
});
|
|
2681
2688
|
}
|
package/dist/p5-phone.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.
|
|
2
|
+
* p5-phone v1.9.2
|
|
3
3
|
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
4
|
* https://github.com/npuckett/p5-phone
|
|
5
5
|
*
|
|
@@ -7,4 +7,4 @@
|
|
|
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,window.vibrationEnabled=!1,window.speechEnabled=!1,window.nfcEnabled=!1,window.nfcError="",window.nfcStatus="idle",window.nfcTagAliases={},window.lastNfcMessage=null,window.lastNfcSerialNumber=null,window.lastNfcAlias="";let _micInstance=null,_nfcReader=null,_nfcAbortController=null;const _p5MajorVersion="undefined"!=typeof p5&&p5.VERSION?parseInt(p5.VERSION.split(".")[0],10):1,_isP5v2=_p5MajorVersion>=2;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 enableSpeechTap(e="Tap to enable speech recognition"){_createTapToEnable(e,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via tap")})}function enableSpeechButton(e="ENABLE SPEECH RECOGNITION",n="Enabling speech recognition..."){_createPermissionButton(e,n,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via button")})}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 enableNfcButton(e="ENABLE NFC",n="Enabling NFC..."){_createPermissionButton(e,n,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via button")})}function enableNfcTap(e="Tap screen to enable NFC"){_createTapToEnable(e,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via tap")})}function enableGyroCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via canvas touch")})}function enableMicCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via canvas touch")})}function enableSoundCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via canvas touch")})}function enableSpeechCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via canvas touch")})}function enableVibrationCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via canvas touch")})}function enableNfcCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via canvas touch")})}function enableAllCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via canvas touch")})}function enableCameraCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via canvas touch")})}function enableGyroBanner(e="Tap to enable motion sensors",n="top"){_createBannerToEnable(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via banner")})}function enableMicBanner(e="Tap to enable microphone",n="top"){_createBannerToEnable(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via banner")})}function enableSoundBanner(e="Tap to enable sound",n="top"){_createBannerToEnable(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via banner")})}function enableSpeechBanner(e="Tap to enable speech recognition",n="top"){_createBannerToEnable(e,n,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via banner")})}function enableVibrationBanner(e="Tap to enable vibration",n="top"){_createBannerToEnable(e,n,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via banner")})}function enableNfcBanner(e="Tap to enable NFC",n="top"){_createBannerToEnable(e,n,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via banner")})}function enableAllBanner(e="Tap to enable sensors & microphone",n="top"){_createBannerToEnable(e,n,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via banner")})}function enableCameraBanner(e="Tap to enable camera",n="top"){_createBannerToEnable(e,n,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via banner")})}function enableGyroOn(e){_bindPermissionTo(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via custom element")})}function enableMicOn(e){_bindPermissionTo(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via custom element")})}function enableSoundOn(e){_bindPermissionTo(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via custom element")})}function enableSpeechOn(e){_bindPermissionTo(e,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via custom element")})}function enableVibrationOn(e){_bindPermissionTo(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via custom element")})}function enableNfcOn(e){_bindPermissionTo(e,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via custom element")})}function enableAllOn(e){_bindPermissionTo(e,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via custom element")})}function enableCameraOn(e){_bindPermissionTo(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via custom element")})}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)}function stopNfc(){_nfcAbortController&&(_nfcAbortController.abort(),_nfcAbortController=null),_nfcReader=null,window.nfcEnabled=!1,window.nfcStatus="stopped",console.log("NFC scanning stopped")}function _normalizeNfcText(e){return null==e?"":String(e).trim()}function _normalizeNfcTagId(e){return _normalizeNfcText(e).toLowerCase()}function _nfcTextMatches(e,n){const o=_normalizeNfcText(e).toLowerCase(),t=_normalizeNfcText(n).toLowerCase();return""!==o&&o===t}function setNfcTagAlias(e,n){const o=_normalizeNfcTagId(e),t=_normalizeNfcText(n);return o?(t?window.nfcTagAliases[o]=t:delete window.nfcTagAliases[o],_normalizeNfcTagId(window.lastNfcSerialNumber)===o&&(window.lastNfcAlias=t,window.lastNfcMessage&&(window.lastNfcMessage.alias=t)),t):(console.warn("p5-phone: setNfcTagAlias() needs an NFC serial number"),"")}function getNfcTagAlias(e=window.lastNfcSerialNumber){const n=_normalizeNfcTagId(e);return n&&window.nfcTagAliases[n]||""}function isNfcTag(e,n=window.lastNfcSerialNumber){const o=_normalizeNfcTagId(n),t=_normalizeNfcText(e);return!(!o||!t)&&(o===_normalizeNfcTagId(t)||_nfcTextMatches(getNfcTagAlias(n),t))}async function _requestMotionPermissionsCore(){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}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0}}async function _requestMicrophonePermissionsCore(){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();")}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e)}}async function _requestSoundOutputCore(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0}}async function _requestSpeechPermissionCore(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.speechEnabled=!0}catch(e){console.error("Speech permission error:",e),_debugVisible&&debugError("Speech permission error:",e)}}async function _requestVibrationPermissionCore(){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"),void(window.vibrationEnabled=!1);navigator.vibrate(1)?(window.vibrationEnabled=!0,console.log("✅ Vibration enabled")):(console.warn("⚠️ Vibration API available but vibration failed"),window.vibrationEnabled=!1)}catch(e){console.error("Vibration permission error:",e),_debugVisible&&debugError("Vibration permission error:",e),window.vibrationEnabled=!1}}async function _requestNfcPermissionCore(){try{return!(!window.nfcEnabled||!_nfcReader)||(window.nfcError="",window.nfcStatus="starting","NDEFReader"in window?(window.nfcStatus="requesting-permission",_nfcAbortController=new AbortController,_nfcReader=new NDEFReader,_nfcReader.onreading=e=>{const n=e.serialNumber||"",o=new TextDecoder,t=[];for(const n of e.message.records){const e={recordType:n.recordType,mediaType:n.mediaType||null,id:n.id||null,data:null,raw:n.data};if("text"===n.recordType||"url"===n.recordType)e.data=o.decode(n.data);else if("mime"===n.recordType&&n.mediaType)try{const t=o.decode(n.data);n.mediaType.includes("json")?e.data=JSON.parse(t):e.data=t}catch(o){e.data=n.data}else e.data=n.data;t.push(e)}const a=getNfcTagAlias(n),i={serialNumber:n,alias:a,records:t};window.lastNfcMessage=i,window.lastNfcSerialNumber=n,window.lastNfcAlias=a,window.nfcStatus="tag-read",window.nfcError="","function"==typeof nfcRead&&nfcRead(i,n),console.log("NFC tag read — serial:",n,"records:",t.length),_debugVisible&&debug("NFC tag read: "+n)},_nfcReader.onreadingerror=e=>{console.warn("⚠️ NFC read error — tag may be incompatible or out of range"),window.nfcError="NFC read error. Make sure the tag is NDEF formatted and hold it near the phone NFC antenna.",_debugVisible&&debugWarn("NFC read error — tag incompatible or out of range")},await _nfcReader.scan({signal:_nfcAbortController.signal}),window.nfcEnabled=!0,window.nfcStatus="scanning",console.log("✅ NFC scanning active"),!0):(console.warn("⚠️ Web NFC API not supported on this device/browser (Android Chrome 89+ required)"),_debugVisible&&debugWarn("Web NFC not supported on this device/browser"),window.nfcEnabled=!1,window.nfcStatus="unsupported",window.nfcError=!1===window.isSecureContext?"NFC requires HTTPS. Serve this sketch from an HTTPS URL, not plain HTTP.":"Web NFC is not supported in this browser. Use Android Chrome 89+ over HTTPS.",!1))}catch(e){return"NotAllowedError"===e.name?(console.warn("⚠️ NFC permission denied by user"),window.nfcStatus="permission-denied",window.nfcError="NFC permission was denied. Reload and tap Allow if Chrome asks.",_debugVisible&&debugWarn("NFC permission denied")):"NotSupportedError"===e.name?(console.warn("⚠️ NFC not supported on this device"),window.nfcStatus="unsupported",window.nfcError="NFC is not supported on this device/browser, or this page is not using HTTPS.",_debugVisible&&debugWarn("NFC not supported on this device")):"SecurityError"===e.name?(console.warn("⚠️ NFC requires a secure HTTPS context"),window.nfcStatus="secure-context-required",window.nfcError="NFC requires HTTPS. Serve this sketch from an HTTPS URL, not plain HTTP.",_debugVisible&&debugWarn("NFC requires HTTPS")):(console.error("NFC permission error:",e),window.nfcStatus="error",window.nfcError=e&&e.message?e.message:"NFC could not start.",_debugVisible&&debugError("NFC error: "+e.message)),window.nfcEnabled=!1,_nfcReader=null,_nfcAbortController=null,!1}}async function _requestMotionPermissions(){await _requestMotionPermissionsCore(),_notifySketchReady()}async function _requestMicrophonePermissions(){await _requestMicrophonePermissionsCore(),_notifySketchReady()}async function _requestSoundOutput(){await _requestSoundOutputCore(),_notifySketchReady()}async function _requestSpeechPermission(){await _requestSpeechPermissionCore(),_notifySketchReady()}async function _requestVibrationPermission(){await _requestVibrationPermissionCore(),_notifySketchReady()}async function _requestNfcPermission(){const e=await _requestNfcPermissionCore();return _notifySketchReady(),e}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,speech:window.speechEnabled,vibration:window.vibrationEnabled,nfc:window.nfcEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,o){_removeExistingUI();let t=!1;const a=document.createElement("button");a.id="permissionButton",a.textContent=e,a.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 ",a.addEventListener("mouseenter",()=>{a.style.transform="translate(-50%, -50%) scale(1.05)"}),a.addEventListener("mouseleave",()=>{a.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{!t&&a.parentNode&&(t=!0,a.style.display="none",i.style.display="block",await o(),i.style.display="none",_removeExistingUI())};a.addEventListener("click",r),a.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),a.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(a),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();let o=!1;const t=document.createElement("div");t.id="tapOverlay",t.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 a=document.createElement("div");a.textContent=e,a.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 ",t.appendChild(a);const i=async()=>{!o&&t.parentNode&&(o=!0,a.textContent="Enabling...",await n(),t.parentNode&&document.body.removeChild(t))};t.addEventListener("click",i),t.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()}),t.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),i()}),document.body.appendChild(t)}function _removeExistingUI(){const e=document.getElementById("permissionButton"),n=document.getElementById("permissionStatus"),o=document.getElementById("tapOverlay"),t=document.getElementById("permissionBanner");e&&e.remove(),n&&n.remove(),o&&o.remove(),t&&t.remove()}function _createCanvasToEnable(e,n){_removeExistingUI();let o=!1,t=null;e&&(t=setInterval(()=>{const n=document.querySelector("canvas");n&&"function"==typeof push&&(push(),fill(255,255,255,200),noStroke(),textAlign(CENTER,CENTER),textSize(.04*Math.min(n.width,n.height)),text(e,("undefined"!=typeof width?width:n.width)/2,.9*("undefined"!=typeof height?height:n.height)),pop())},50));const a=async e=>{o||(o=!0,t&&(clearInterval(t),t=null),document.removeEventListener("touchstart",a,!0),document.removeEventListener("mousedown",a,!0),await n())},i=()=>{const e=document.querySelector("canvas");e?(e.addEventListener("touchstart",a,{once:!0,capture:!0}),e.addEventListener("mousedown",a,{once:!0,capture:!0})):setTimeout(i,50)};i()}function _createBannerToEnable(e,n,o){_removeExistingUI();let t=!1;const a=document.createElement("div");a.id="permissionBanner";const i="top"===n;a.style.cssText=`\n position: fixed;\n ${i?"top: 0;":"bottom: 0;"}\n left: 0;\n width: 100%;\n padding: 16px 20px;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n font-size: 16px;\n font-weight: 600;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n z-index: 999999;\n cursor: pointer;\n touch-action: manipulation;\n box-shadow: ${i?"0 2px 10px rgba(0,0,0,0.3)":"0 -2px 10px rgba(0,0,0,0.3)"};\n transition: opacity 0.3s ease, transform 0.3s ease;\n transform: translateY(${i?"-100%":"100%"});\n `,a.textContent=e,document.body.appendChild(a),requestAnimationFrame(()=>{requestAnimationFrame(()=>{a.style.transform="translateY(0)"})});const r=async()=>{!t&&a.parentNode&&(t=!0,a.textContent="Enabling...",a.style.pointerEvents="none",await o(),a.style.transform=`translateY(${i?"-100%":"100%"})`,a.style.opacity="0",setTimeout(()=>{a.parentNode&&a.remove()},300))};a.addEventListener("click",r),a.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),a.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()})}function _bindPermissionTo(e,n){let o=!1;const t=()=>{const a=document.querySelector(e);if(!a)return console.warn(`p5-phone: Element "${e}" not found. Retrying...`),void setTimeout(t,100);const i=async()=>{o||(o=!0,await n())};a.addEventListener("click",i),a.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()})};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",t):t()}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,a=o.touches[0].clientY,i=t-e,r=a-n;(e<20&&i>0||e>window.innerWidth-20&&i<0)&&(o.preventDefault(),o.stopPropagation()),0===window.pageYOffset&&r>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(){let e=0;const n=()=>{e++;"undefined"!=typeof p5&&p5.instance||document.querySelector("canvas")||"function"==typeof window.setup&&"function"==typeof window.draw?_overrideP5Touch():e<50?setTimeout(n,100):console.warn("p5-phone: Could not detect p5.js setup completion. Touch overrides not applied.")};setTimeout(n,100)}function _overrideP5Touch(){const e=window.mousePressed||function(){},n=window.mouseDragged||function(){},o=window.mouseReleased||function(){};if(!_isP5v2){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},o=window.touchEnded||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(n){return e(n),!1},window.mouseDragged=function(e){return n(e),!1},window.mouseReleased=function(e){return o(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,"function"==typeof console.error&&(window._originalConsoleError=console.error),"function"==typeof console.warn&&(window._originalConsoleWarn=console.warn),console.error=function(...e){try{window._originalConsoleError.apply(console,e)}catch(e){}_debugVisible&&debugError(...e)},console.warn=function(...e){try{window._originalConsoleWarn.apply(console,e)}catch(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.enableSpeechTap=enableSpeechTap,window.enableSpeechButton=enableSpeechButton,window.enableVibrationTap=enableVibrationTap,window.enableVibrationButton=enableVibrationButton,window.vibrate=vibrate,window.stopVibration=stopVibration,window.enableNfcTap=enableNfcTap,window.enableNfcButton=enableNfcButton,window.stopNfc=stopNfc,window.setNfcTagAlias=setNfcTagAlias,window.getNfcTagAlias=getNfcTagAlias,window.isNfcTag=isNfcTag,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton,window.enableGyroCanvas=enableGyroCanvas,window.enableMicCanvas=enableMicCanvas,window.enableSoundCanvas=enableSoundCanvas,window.enableSpeechCanvas=enableSpeechCanvas,window.enableVibrationCanvas=enableVibrationCanvas,window.enableNfcCanvas=enableNfcCanvas,window.enableAllCanvas=enableAllCanvas,window.enableCameraCanvas=enableCameraCanvas,window.enableGyroBanner=enableGyroBanner,window.enableMicBanner=enableMicBanner,window.enableSoundBanner=enableSoundBanner,window.enableSpeechBanner=enableSpeechBanner,window.enableVibrationBanner=enableVibrationBanner,window.enableNfcBanner=enableNfcBanner,window.enableAllBanner=enableAllBanner,window.enableCameraBanner=enableCameraBanner,window.enableGyroOn=enableGyroOn,window.enableMicOn=enableMicOn,window.enableSoundOn=enableSoundOn,window.enableSpeechOn=enableSpeechOn,window.enableVibrationOn=enableVibrationOn,window.enableNfcOn=enableNfcOn,window.enableAllOn=enableAllOn,window.enableCameraOn=enableCameraOn;class PhoneCamera{constructor(e="user",n=!0,o="fitHeight"){this._active=e,this._mirror=n,this._mode=o,this._fixedWidth=640,this._fixedHeight=480,this._video=null,this._ready=!1,this._p5Instance=window,this._onReadyCallback=null,this._createCaptureRef=null,window._phoneCameras||(window._phoneCameras=[]),window._phoneCameras.push(this)}get ready(){return this._ready}get video(){return this._video}get videoElement(){return this._video?this._video.elt:null}get width(){if(!this._ready)return 0;return this.getDimensions().width}get height(){if(!this._ready)return 0;return this.getDimensions().height}get active(){return this._active}set active(e){"user"===e||"environment"===e?this._active!==e&&(this._active=e,this._ready&&this._switchCamera()):console.error('PhoneCamera: active must be "user" or "environment"')}get mirror(){return this._mirror}set mirror(e){this._mirror=!!e}get mode(){return this._mode}set mode(e){const n=["fitWidth","fitHeight","cover","contain","fixed"];n.includes(e)?this._mode=e:console.error("PhoneCamera: mode must be one of:",n.join(", "))}get fixedWidth(){return this._fixedWidth}set fixedWidth(e){this._fixedWidth=Math.max(1,e)}get fixedHeight(){return this._fixedHeight}set fixedHeight(e){this._fixedHeight=Math.max(1,e)}onReady(e){this._onReadyCallback=e,this._ready&&this._video&&this._video.elt&&this._video.elt.readyState>=2?e():this._video&&this._checkVideoReady()}_initializeCamera(){if(this._ready||this._video)return;const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log("✅ PhoneCamera ready"),this._checkVideoReady()}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0,this._checkVideoReady()})}_checkVideoReady(e=0){if(this._video&&this._video.elt&&this._video.elt.readyState>=2){if(this._onReadyCallback){const e=this._onReadyCallback;this._onReadyCallback=null,e()}}else e<100?setTimeout(()=>this._checkVideoReady(e+1),100):(console.warn("PhoneCamera: Video failed to reach ready state after 10 seconds"),_debugVisible&&debugWarn("PhoneCamera: Video not ready after timeout. Check camera permissions."))}_switchCamera(){if(!this._video)return;this._ready;this._ready=!1,this._video.remove();const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log(`✅ PhoneCamera switched to ${this._active} camera`)}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0})}remove(){this._video&&(this._video.remove(),this._video=null),this._ready=!1}getDimensions(){if(!this._ready||!this._video)return{x:0,y:0,width:0,height:0,scaleX:1,scaleY:1};const e=this._video.width,n=this._video.height,o="undefined"!=typeof width?width:window.innerWidth,t="undefined"!=typeof height?height:window.innerHeight;let a,i,r,s;if("fixed"===this._mode)a=this._fixedWidth,i=this._fixedHeight,r=(o-a)/2,s=(t-i)/2;else if("fitWidth"===this._mode)a=o,i=n/e*a,r=0,s=(t-i)/2;else if("fitHeight"===this._mode)i=t,a=e/n*i,r=(o-a)/2,s=0;else if("cover"===this._mode){const l=Math.max(o/e,t/n);a=e*l,i=n*l,r=(o-a)/2,s=(t-i)/2}else if("contain"===this._mode){const l=Math.min(o/e,t/n);a=e*l,i=n*l,r=(o-a)/2,s=(t-i)/2}return{x:r,y:s,width:a,height:i,scaleX:a/e,scaleY:i/n}}mapPoint(e,n){const o=this.getDimensions();let t=e*o.scaleX;const a=n*o.scaleY;this._mirror&&(t=o.width-t);return{x:t+o.x,y:a+o.y}}mapKeypoint(e){if(!e||void 0===e.x||void 0===e.y)return console.warn("PhoneCamera.mapKeypoint: invalid keypoint",e),e;const n=this.mapPoint(e.x,e.y);return{...e,x:n.x,y:n.y}}mapKeypoints(e){return Array.isArray(e)?e.map(e=>this.mapKeypoint(e)):(console.warn("PhoneCamera.mapKeypoints: expected array, got",typeof e),e)}mapBox(e){if(!e)return console.warn("PhoneCamera.mapBox: invalid box",e),e;const n=void 0!==e.x?e.x:e.xMin,o=void 0!==e.y?e.y:e.yMin,t=void 0!==e.width?e.width:e.xMax-e.xMin,a=void 0!==e.height?e.height:e.yMax-e.yMin,i=Number(n),r=Number(o),s=Number(t),l=Number(a);if(!(Number.isFinite(i)&&Number.isFinite(r)&&Number.isFinite(s)&&Number.isFinite(l)))return console.warn("PhoneCamera.mapBox: invalid box",e),e;const d=this.mapPoint(i,r),c=this.mapPoint(i+s,r+l),u=Math.min(d.x,c.x),p=Math.min(d.y,c.y),b=Math.abs(c.x-d.x),w=Math.abs(c.y-d.y);return{...e,x:u,y:p,width:b,height:w,xMin:u,yMin:p,xMax:u+b,yMax:p+w}}mapBoxes(e){return Array.isArray(e)?e.map(e=>this.mapBox(e)):(console.warn("PhoneCamera.mapBoxes: expected array, got",typeof e),e)}_draw(){if(!this._ready||!this._video)return;const e=this.getDimensions(),n="undefined"!=typeof width?width:window.innerWidth;this._drawDebugLogged||(console.log("_draw() params:",{x:e.x,y:e.y,width:e.width,height:e.height,canvasWidth:n,mirror:this._mirror}),this._drawDebugLogged=!0),push(),this._mirror?(translate(n,0),scale(-1,1),image(this._video,e.x,e.y,e.width,e.height)):image(this._video,e.x,e.y,e.width,e.height),pop()}}function createPhoneCamera(e="user",n=!0,o="fitHeight"){return new PhoneCamera(e,n,o)}function enableCameraButton(e="ENABLE CAMERA",n="Starting camera..."){_createPermissionButton(e,n,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via button")})}function enableCameraTap(e="Tap screen to enable camera"){navigator.permissions&&navigator.permissions.query?navigator.permissions.query({name:"camera"}).then(n=>{"granted"===n.state?(console.log("✅ Camera permission already granted - auto-starting"),_requestCameraPermission()):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}).catch(()=>{_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}async function _requestCameraPermission(){try{if(void 0!==window._phoneCameras&&Array.isArray(window._phoneCameras))for(let e of window._phoneCameras)!e||e._ready||e._video||e._initializeCamera();"function"==typeof userCameraReady&&userCameraReady(),_notifySketchReady()}catch(e){console.error("Camera permission error:",e),_debugVisible&&debugError("Camera permission error:",e),_notifySketchReady()}}if(window.createPhoneCamera=createPhoneCamera,window.enableCameraButton=enableCameraButton,window.enableCameraTap=enableCameraTap,"undefined"!=typeof p5&&p5.prototype){const e=p5.prototype.image;p5.prototype.image=function(...n){if(n[0]instanceof PhoneCamera){n[0]._draw()}else e.apply(this,n)}}"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.enableSpeechTap=enableSpeechTap,p5.prototype.enableSpeechButton=enableSpeechButton,p5.prototype.enableVibrationTap=enableVibrationTap,p5.prototype.enableVibrationButton=enableVibrationButton,p5.prototype.vibrate=vibrate,p5.prototype.stopVibration=stopVibration,p5.prototype.enableNfcTap=enableNfcTap,p5.prototype.enableNfcButton=enableNfcButton,p5.prototype.stopNfc=stopNfc,p5.prototype.setNfcTagAlias=setNfcTagAlias,p5.prototype.getNfcTagAlias=getNfcTagAlias,p5.prototype.isNfcTag=isNfcTag,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,p5.prototype.enableGyroCanvas=enableGyroCanvas,p5.prototype.enableMicCanvas=enableMicCanvas,p5.prototype.enableSoundCanvas=enableSoundCanvas,p5.prototype.enableSpeechCanvas=enableSpeechCanvas,p5.prototype.enableVibrationCanvas=enableVibrationCanvas,p5.prototype.enableNfcCanvas=enableNfcCanvas,p5.prototype.enableAllCanvas=enableAllCanvas,p5.prototype.enableCameraCanvas=enableCameraCanvas,p5.prototype.enableGyroBanner=enableGyroBanner,p5.prototype.enableMicBanner=enableMicBanner,p5.prototype.enableSoundBanner=enableSoundBanner,p5.prototype.enableSpeechBanner=enableSpeechBanner,p5.prototype.enableVibrationBanner=enableVibrationBanner,p5.prototype.enableNfcBanner=enableNfcBanner,p5.prototype.enableAllBanner=enableAllBanner,p5.prototype.enableCameraBanner=enableCameraBanner,p5.prototype.enableGyroOn=enableGyroOn,p5.prototype.enableMicOn=enableMicOn,p5.prototype.enableSoundOn=enableSoundOn,p5.prototype.enableSpeechOn=enableSpeechOn,p5.prototype.enableVibrationOn=enableVibrationOn,p5.prototype.enableNfcOn=enableNfcOn,p5.prototype.enableAllOn=enableAllOn,p5.prototype.enableCameraOn=enableCameraOn,p5.prototype.createPhoneCamera=createPhoneCamera,p5.prototype.enableCameraButton=enableCameraButton,p5.prototype.enableCameraTap=enableCameraTap,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")),"undefined"!=typeof p5&&"function"==typeof p5.registerAddon&&p5.registerAddon(function(e,n,o){n.lockGestures=lockGestures,n.enableGyroTap=enableGyroTap,n.enableGyroButton=enableGyroButton,n.enableMicTap=enableMicTap,n.enableMicButton=enableMicButton,n.enableSoundTap=enableSoundTap,n.enableSoundButton=enableSoundButton,n.enableSpeechTap=enableSpeechTap,n.enableSpeechButton=enableSpeechButton,n.enableVibrationTap=enableVibrationTap,n.enableVibrationButton=enableVibrationButton,n.vibrate=vibrate,n.stopVibration=stopVibration,n.enableNfcTap=enableNfcTap,n.enableNfcButton=enableNfcButton,n.stopNfc=stopNfc,n.setNfcTagAlias=setNfcTagAlias,n.getNfcTagAlias=getNfcTagAlias,n.isNfcTag=isNfcTag,n.enableAllTap=enableAllTap,n.enableAllButton=enableAllButton,n.enableGyroCanvas=enableGyroCanvas,n.enableMicCanvas=enableMicCanvas,n.enableSoundCanvas=enableSoundCanvas,n.enableSpeechCanvas=enableSpeechCanvas,n.enableVibrationCanvas=enableVibrationCanvas,n.enableNfcCanvas=enableNfcCanvas,n.enableAllCanvas=enableAllCanvas,n.enableCameraCanvas=enableCameraCanvas,n.enableGyroBanner=enableGyroBanner,n.enableMicBanner=enableMicBanner,n.enableSoundBanner=enableSoundBanner,n.enableSpeechBanner=enableSpeechBanner,n.enableVibrationBanner=enableVibrationBanner,n.enableNfcBanner=enableNfcBanner,n.enableAllBanner=enableAllBanner,n.enableCameraBanner=enableCameraBanner,n.enableGyroOn=enableGyroOn,n.enableMicOn=enableMicOn,n.enableSoundOn=enableSoundOn,n.enableSpeechOn=enableSpeechOn,n.enableVibrationOn=enableVibrationOn,n.enableNfcOn=enableNfcOn,n.enableAllOn=enableAllOn,n.enableCameraOn=enableCameraOn,n.createPhoneCamera=createPhoneCamera,n.enableCameraButton=enableCameraButton,n.enableCameraTap=enableCameraTap,n.showDebug=showDebug,n.hideDebug=hideDebug,n.toggleDebug=toggleDebug,n.debug=debug,n.debugError=debugError,n.debugWarn=debugWarn,console.log("✅ Mobile p5.js Permissions: registered as p5.js 2.0 addon")});
|
|
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,window.speechEnabled=!1,window.nfcEnabled=!1,window.nfcError="",window.nfcStatus="idle",window.nfcTagAliases={},window.lastNfcMessage=null,window.lastNfcSerialNumber=null,window.lastNfcAlias="";let _micInstance=null,_nfcReader=null,_nfcAbortController=null;const _p5MajorVersion="undefined"!=typeof p5&&p5.VERSION?parseInt(p5.VERSION.split(".")[0],10):1,_isP5v2=_p5MajorVersion>=2;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 enableSpeechTap(e="Tap to enable speech recognition"){_createTapToEnable(e,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via tap")})}function enableSpeechButton(e="ENABLE SPEECH RECOGNITION",n="Enabling speech recognition..."){_createPermissionButton(e,n,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via button")})}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 enableNfcButton(e="ENABLE NFC",n="Enabling NFC..."){_createPermissionButton(e,n,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via button")})}function enableNfcTap(e="Tap screen to enable NFC"){_createTapToEnable(e,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via tap")})}function enableGyroCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via canvas touch")})}function enableMicCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via canvas touch")})}function enableSoundCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via canvas touch")})}function enableSpeechCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via canvas touch")})}function enableVibrationCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via canvas touch")})}function enableNfcCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via canvas touch")})}function enableAllCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via canvas touch")})}function enableCameraCanvas(e="Touch to start"){_createCanvasToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via canvas touch")})}function enableGyroBanner(e="Tap to enable motion sensors",n="top"){_createBannerToEnable(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via banner")})}function enableMicBanner(e="Tap to enable microphone",n="top"){_createBannerToEnable(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via banner")})}function enableSoundBanner(e="Tap to enable sound",n="top"){_createBannerToEnable(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via banner")})}function enableSpeechBanner(e="Tap to enable speech recognition",n="top"){_createBannerToEnable(e,n,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via banner")})}function enableVibrationBanner(e="Tap to enable vibration",n="top"){_createBannerToEnable(e,n,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via banner")})}function enableNfcBanner(e="Tap to enable NFC",n="top"){_createBannerToEnable(e,n,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via banner")})}function enableAllBanner(e="Tap to enable sensors & microphone",n="top"){_createBannerToEnable(e,n,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via banner")})}function enableCameraBanner(e="Tap to enable camera",n="top"){_createBannerToEnable(e,n,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via banner")})}function enableGyroOn(e){_bindPermissionTo(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via custom element")})}function enableMicOn(e){_bindPermissionTo(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via custom element")})}function enableSoundOn(e){_bindPermissionTo(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via custom element")})}function enableSpeechOn(e){_bindPermissionTo(e,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via custom element")})}function enableVibrationOn(e){_bindPermissionTo(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via custom element")})}function enableNfcOn(e){_bindPermissionTo(e,async()=>{await _requestNfcPermission(),console.log("✅ NFC enabled via custom element")})}function enableAllOn(e){_bindPermissionTo(e,async()=>{await _requestMotionPermissionsCore(),await _requestMicrophonePermissionsCore(),_notifySketchReady(),console.log("✅ Motion sensors and microphone enabled via custom element")})}function enableCameraOn(e){_bindPermissionTo(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via custom element")})}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)}function stopNfc(){_nfcAbortController&&(_nfcAbortController.abort(),_nfcAbortController=null),_nfcReader=null,window.nfcEnabled=!1,window.nfcStatus="stopped",console.log("NFC scanning stopped")}function _normalizeNfcText(e){return null==e?"":String(e).trim()}function _normalizeNfcTagId(e){return _normalizeNfcText(e).toLowerCase()}function _nfcTextMatches(e,n){const o=_normalizeNfcText(e).toLowerCase(),t=_normalizeNfcText(n).toLowerCase();return""!==o&&o===t}function setNfcTagAlias(e,n){const o=_normalizeNfcTagId(e),t=_normalizeNfcText(n);return o?(t?window.nfcTagAliases[o]=t:delete window.nfcTagAliases[o],_normalizeNfcTagId(window.lastNfcSerialNumber)===o&&(window.lastNfcAlias=t,window.lastNfcMessage&&(window.lastNfcMessage.alias=t)),t):(console.warn("p5-phone: setNfcTagAlias() needs an NFC serial number"),"")}function getNfcTagAlias(e=window.lastNfcSerialNumber){const n=_normalizeNfcTagId(e);return n&&window.nfcTagAliases[n]||""}function isNfcTag(e,n=window.lastNfcSerialNumber){const o=_normalizeNfcTagId(n),t=_normalizeNfcText(e);return!(!o||!t)&&(o===_normalizeNfcTagId(t)||_nfcTextMatches(getNfcTagAlias(n),t))}async function _requestMotionPermissionsCore(){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}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0}}async function _requestMicrophonePermissionsCore(){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();")}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e)}}async function _requestSoundOutputCore(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0}}async function _requestSpeechPermissionCore(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.speechEnabled=!0}catch(e){console.error("Speech permission error:",e),_debugVisible&&debugError("Speech permission error:",e)}}async function _requestVibrationPermissionCore(){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"),void(window.vibrationEnabled=!1);navigator.vibrate(1)?(window.vibrationEnabled=!0,console.log("✅ Vibration enabled")):(console.warn("⚠️ Vibration API available but vibration failed"),window.vibrationEnabled=!1)}catch(e){console.error("Vibration permission error:",e),_debugVisible&&debugError("Vibration permission error:",e),window.vibrationEnabled=!1}}async function _requestNfcPermissionCore(){try{return!(!window.nfcEnabled||!_nfcReader)||(window.nfcError="",window.nfcStatus="starting","NDEFReader"in window?(window.nfcStatus="requesting-permission",_nfcAbortController=new AbortController,_nfcReader=new NDEFReader,_nfcReader.onreading=e=>{const n=e.serialNumber||"",o=new TextDecoder,t=[];for(const n of e.message.records){const e={recordType:n.recordType,mediaType:n.mediaType||null,id:n.id||null,data:null,raw:n.data};if("text"===n.recordType||"url"===n.recordType)e.data=o.decode(n.data);else if("mime"===n.recordType&&n.mediaType)try{const t=o.decode(n.data);n.mediaType.includes("json")?e.data=JSON.parse(t):e.data=t}catch(o){e.data=n.data}else e.data=n.data;t.push(e)}const a=getNfcTagAlias(n),i={serialNumber:n,alias:a,records:t};window.lastNfcMessage=i,window.lastNfcSerialNumber=n,window.lastNfcAlias=a,window.nfcStatus="tag-read",window.nfcError="","function"==typeof nfcRead&&nfcRead(i,n),console.log("NFC tag read — serial:",n,"records:",t.length),_debugVisible&&debug("NFC tag read: "+n)},_nfcReader.onreadingerror=e=>{console.warn("⚠️ NFC read error — tag may be incompatible or out of range"),window.nfcError="NFC read error. Make sure the tag is NDEF formatted and hold it near the phone NFC antenna.",_debugVisible&&debugWarn("NFC read error — tag incompatible or out of range")},await _nfcReader.scan({signal:_nfcAbortController.signal}),window.nfcEnabled=!0,window.nfcStatus="scanning",console.log("✅ NFC scanning active"),!0):(console.warn("⚠️ Web NFC API not supported on this device/browser (Android Chrome 89+ required)"),_debugVisible&&debugWarn("Web NFC not supported on this device/browser"),window.nfcEnabled=!1,window.nfcStatus="unsupported",window.nfcError=!1===window.isSecureContext?"NFC requires HTTPS. Serve this sketch from an HTTPS URL, not plain HTTP.":"Web NFC is not supported in this browser. Use Android Chrome 89+ over HTTPS.",!1))}catch(e){return"NotAllowedError"===e.name?(console.warn("⚠️ NFC permission denied by user"),window.nfcStatus="permission-denied",window.nfcError="NFC permission was denied. Reload and tap Allow if Chrome asks.",_debugVisible&&debugWarn("NFC permission denied")):"NotSupportedError"===e.name?(console.warn("⚠️ NFC not supported on this device"),window.nfcStatus="unsupported",window.nfcError="NFC is not supported on this device/browser, or this page is not using HTTPS.",_debugVisible&&debugWarn("NFC not supported on this device")):"SecurityError"===e.name?(console.warn("⚠️ NFC requires a secure HTTPS context"),window.nfcStatus="secure-context-required",window.nfcError="NFC requires HTTPS. Serve this sketch from an HTTPS URL, not plain HTTP.",_debugVisible&&debugWarn("NFC requires HTTPS")):(console.error("NFC permission error:",e),window.nfcStatus="error",window.nfcError=e&&e.message?e.message:"NFC could not start.",_debugVisible&&debugError("NFC error: "+e.message)),window.nfcEnabled=!1,_nfcReader=null,_nfcAbortController=null,!1}}async function _requestMotionPermissions(){await _requestMotionPermissionsCore(),_notifySketchReady()}async function _requestMicrophonePermissions(){await _requestMicrophonePermissionsCore(),_notifySketchReady()}async function _requestSoundOutput(){await _requestSoundOutputCore(),_notifySketchReady()}async function _requestSpeechPermission(){await _requestSpeechPermissionCore(),_notifySketchReady()}async function _requestVibrationPermission(){await _requestVibrationPermissionCore(),_notifySketchReady()}async function _requestNfcPermission(){const e=await _requestNfcPermissionCore();return _notifySketchReady(),e}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,speech:window.speechEnabled,vibration:window.vibrationEnabled,nfc:window.nfcEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,o){_removeExistingUI();let t=!1;const a=document.createElement("button");a.id="permissionButton",a.textContent=e,a.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 ",a.addEventListener("mouseenter",()=>{a.style.transform="translate(-50%, -50%) scale(1.05)"}),a.addEventListener("mouseleave",()=>{a.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{!t&&a.parentNode&&(t=!0,a.style.display="none",i.style.display="block",await o(),i.style.display="none",_removeExistingUI())};a.addEventListener("click",r),a.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),a.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(a),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();let o=!1;const t=document.createElement("div");t.id="tapOverlay",t.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 a=document.createElement("div");a.textContent=e,a.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 ",t.appendChild(a);const i=async()=>{!o&&t.parentNode&&(o=!0,a.textContent="Enabling...",await n(),t.parentNode&&document.body.removeChild(t))};t.addEventListener("click",i),t.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()}),t.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),i()}),document.body.appendChild(t)}function _removeExistingUI(){const e=document.getElementById("permissionButton"),n=document.getElementById("permissionStatus"),o=document.getElementById("tapOverlay"),t=document.getElementById("permissionBanner");e&&e.remove(),n&&n.remove(),o&&o.remove(),t&&t.remove()}function _createCanvasToEnable(e,n){_removeExistingUI();let o=!1,t=null;e&&(t=setInterval(()=>{const n=document.querySelector("canvas");n&&"function"==typeof push&&(push(),fill(255,255,255,200),noStroke(),textAlign(CENTER,CENTER),textSize(.04*Math.min(n.width,n.height)),text(e,("undefined"!=typeof width?width:n.width)/2,.9*("undefined"!=typeof height?height:n.height)),pop())},50));const a=async e=>{o||(o=!0,t&&(clearInterval(t),t=null),document.removeEventListener("touchstart",a,!0),document.removeEventListener("mousedown",a,!0),await n())},i=()=>{const e=document.querySelector("canvas");e?(e.addEventListener("touchstart",a,{once:!0,capture:!0}),e.addEventListener("mousedown",a,{once:!0,capture:!0})):setTimeout(i,50)};i()}function _createBannerToEnable(e,n,o){_removeExistingUI();let t=!1;const a=document.createElement("div");a.id="permissionBanner";const i="top"===n;a.style.cssText=`\n position: fixed;\n ${i?"top: 0;":"bottom: 0;"}\n left: 0;\n width: 100%;\n padding: 16px 20px;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n font-size: 16px;\n font-weight: 600;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n z-index: 999999;\n cursor: pointer;\n touch-action: manipulation;\n box-shadow: ${i?"0 2px 10px rgba(0,0,0,0.3)":"0 -2px 10px rgba(0,0,0,0.3)"};\n transition: opacity 0.3s ease, transform 0.3s ease;\n transform: translateY(${i?"-100%":"100%"});\n `,a.textContent=e,document.body.appendChild(a),requestAnimationFrame(()=>{requestAnimationFrame(()=>{a.style.transform="translateY(0)"})});const r=async()=>{!t&&a.parentNode&&(t=!0,a.textContent="Enabling...",a.style.pointerEvents="none",await o(),a.style.transform=`translateY(${i?"-100%":"100%"})`,a.style.opacity="0",setTimeout(()=>{a.parentNode&&a.remove()},300))};a.addEventListener("click",r),a.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),a.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()})}function _bindPermissionTo(e,n){let o=!1;const t=()=>{const a=document.querySelector(e);if(!a)return console.warn(`p5-phone: Element "${e}" not found. Retrying...`),void setTimeout(t,100);const i=async()=>{o||(o=!0,await n())};a.addEventListener("click",i),a.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()})};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",t):t()}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,a=o.touches[0].clientY,i=t-e,r=a-n;(e<20&&i>0||e>window.innerWidth-20&&i<0)&&(o.preventDefault(),o.stopPropagation()),0===window.pageYOffset&&r>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(){let e=0;const n=()=>{e++;"undefined"!=typeof p5&&p5.instance||document.querySelector("canvas")||"function"==typeof window.setup&&"function"==typeof window.draw?_overrideP5Touch():e<50?setTimeout(n,100):console.warn("p5-phone: Could not detect p5.js setup completion. Touch overrides not applied.")};setTimeout(n,100)}function _overrideP5Touch(){const e=window.mousePressed||function(){},n=window.mouseDragged||function(){},o=window.mouseReleased||function(){};if(!_isP5v2){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},o=window.touchEnded||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(n){return e(n),!1},window.mouseDragged=function(e){return n(e),!1},window.mouseReleased=function(e){return o(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,"function"==typeof console.error&&(window._originalConsoleError=console.error),"function"==typeof console.warn&&(window._originalConsoleWarn=console.warn),console.error=function(...e){try{window._originalConsoleError.apply(console,e)}catch(e){}_debugVisible&&debugError(...e)},console.warn=function(...e){try{window._originalConsoleWarn.apply(console,e)}catch(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.enableSpeechTap=enableSpeechTap,window.enableSpeechButton=enableSpeechButton,window.enableVibrationTap=enableVibrationTap,window.enableVibrationButton=enableVibrationButton,window.vibrate=vibrate,window.stopVibration=stopVibration,window.enableNfcTap=enableNfcTap,window.enableNfcButton=enableNfcButton,window.stopNfc=stopNfc,window.setNfcTagAlias=setNfcTagAlias,window.getNfcTagAlias=getNfcTagAlias,window.isNfcTag=isNfcTag,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton,window.enableGyroCanvas=enableGyroCanvas,window.enableMicCanvas=enableMicCanvas,window.enableSoundCanvas=enableSoundCanvas,window.enableSpeechCanvas=enableSpeechCanvas,window.enableVibrationCanvas=enableVibrationCanvas,window.enableNfcCanvas=enableNfcCanvas,window.enableAllCanvas=enableAllCanvas,window.enableCameraCanvas=enableCameraCanvas,window.enableGyroBanner=enableGyroBanner,window.enableMicBanner=enableMicBanner,window.enableSoundBanner=enableSoundBanner,window.enableSpeechBanner=enableSpeechBanner,window.enableVibrationBanner=enableVibrationBanner,window.enableNfcBanner=enableNfcBanner,window.enableAllBanner=enableAllBanner,window.enableCameraBanner=enableCameraBanner,window.enableGyroOn=enableGyroOn,window.enableMicOn=enableMicOn,window.enableSoundOn=enableSoundOn,window.enableSpeechOn=enableSpeechOn,window.enableVibrationOn=enableVibrationOn,window.enableNfcOn=enableNfcOn,window.enableAllOn=enableAllOn,window.enableCameraOn=enableCameraOn;class PhoneCamera{constructor(e="user",n=!0,o="fitHeight"){this._active=e,this._mirror=n,this._mode=o,this._fixedWidth=640,this._fixedHeight=480,this._video=null,this._ready=!1,this._p5Instance=window,this._onReadyCallback=null,this._createCaptureRef=null,window._phoneCameras||(window._phoneCameras=[]),window._phoneCameras.push(this)}get ready(){return this._ready}get video(){return this._video}get videoElement(){return this._video?this._video.elt:null}get width(){if(!this._ready)return 0;return this.getDimensions().width}get height(){if(!this._ready)return 0;return this.getDimensions().height}get active(){return this._active}set active(e){"user"===e||"environment"===e?this._active!==e&&(this._active=e,this._ready&&this._switchCamera()):console.error('PhoneCamera: active must be "user" or "environment"')}get mirror(){return this._mirror}set mirror(e){this._mirror=!!e}get mode(){return this._mode}set mode(e){const n=["fitWidth","fitHeight","cover","contain","fixed"];n.includes(e)?this._mode=e:console.error("PhoneCamera: mode must be one of:",n.join(", "))}get fixedWidth(){return this._fixedWidth}set fixedWidth(e){this._fixedWidth=Math.max(1,e)}get fixedHeight(){return this._fixedHeight}set fixedHeight(e){this._fixedHeight=Math.max(1,e)}onReady(e){this._onReadyCallback=e,this._ready&&this._video&&this._video.elt&&this._video.elt.readyState>=2?e():this._video&&this._checkVideoReady()}_initializeCamera(){if(this._ready||this._video)return;const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log("✅ PhoneCamera ready"),this._checkVideoReady()}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0,this._checkVideoReady()})}_checkVideoReady(e=0){if(this._video&&this._video.elt&&this._video.elt.readyState>=2){if(this._onReadyCallback){const e=this._onReadyCallback;this._onReadyCallback=null,e()}}else e<100?setTimeout(()=>this._checkVideoReady(e+1),100):(console.warn("PhoneCamera: Video failed to reach ready state after 10 seconds"),_debugVisible&&debugWarn("PhoneCamera: Video not ready after timeout. Check camera permissions."))}_switchCamera(){if(!this._video)return;this._ready;this._ready=!1,this._video.remove();const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log(`✅ PhoneCamera switched to ${this._active} camera`)}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0})}remove(){this._video&&(this._video.remove(),this._video=null),this._ready=!1}getDimensions(){if(!this._ready||!this._video)return{x:0,y:0,width:0,height:0,scaleX:1,scaleY:1};const e=this._video.width,n=this._video.height,o="undefined"!=typeof width?width:window.innerWidth,t="undefined"!=typeof height?height:window.innerHeight;let a,i,r,s;if("fixed"===this._mode)a=this._fixedWidth,i=this._fixedHeight,r=(o-a)/2,s=(t-i)/2;else if("fitWidth"===this._mode)a=o,i=n/e*a,r=0,s=(t-i)/2;else if("fitHeight"===this._mode)i=t,a=e/n*i,r=(o-a)/2,s=0;else if("cover"===this._mode){const l=Math.max(o/e,t/n);a=e*l,i=n*l,r=(o-a)/2,s=(t-i)/2}else if("contain"===this._mode){const l=Math.min(o/e,t/n);a=e*l,i=n*l,r=(o-a)/2,s=(t-i)/2}return{x:r,y:s,width:a,height:i,scaleX:a/e,scaleY:i/n}}mapPoint(e,n){const o=this.getDimensions();let t=e*o.scaleX;const a=n*o.scaleY;this._mirror&&(t=o.width-t);return{x:t+o.x,y:a+o.y}}mapKeypoint(e){if(!e||void 0===e.x||void 0===e.y)return console.warn("PhoneCamera.mapKeypoint: invalid keypoint",e),e;const n=this.mapPoint(e.x,e.y);return{...e,x:n.x,y:n.y}}mapKeypoints(e){return Array.isArray(e)?e.map(e=>this.mapKeypoint(e)):(console.warn("PhoneCamera.mapKeypoints: expected array, got",typeof e),e)}mapBox(e){if(!e)return console.warn("PhoneCamera.mapBox: invalid box",e),e;const n=void 0!==e.x?e.x:e.xMin,o=void 0!==e.y?e.y:e.yMin,t=void 0!==e.width?e.width:e.xMax-e.xMin,a=void 0!==e.height?e.height:e.yMax-e.yMin,i=Number(n),r=Number(o),s=Number(t),l=Number(a);if(!(Number.isFinite(i)&&Number.isFinite(r)&&Number.isFinite(s)&&Number.isFinite(l)))return console.warn("PhoneCamera.mapBox: invalid box",e),e;const d=this.mapPoint(i,r),c=this.mapPoint(i+s,r+l),u=Math.min(d.x,c.x),p=Math.min(d.y,c.y),b=Math.abs(c.x-d.x),h=Math.abs(c.y-d.y);return{...e,x:u,y:p,width:b,height:h,xMin:u,yMin:p,xMax:u+b,yMax:p+h}}mapBoxes(e){return Array.isArray(e)?e.map(e=>this.mapBox(e)):(console.warn("PhoneCamera.mapBoxes: expected array, got",typeof e),e)}_draw(){if(!this._ready||!this._video)return;const e=this.getDimensions(),n="undefined"!=typeof width?width:window.innerWidth;this._drawDebugLogged||(console.log("_draw() params:",{x:e.x,y:e.y,width:e.width,height:e.height,canvasWidth:n,mirror:this._mirror}),this._drawDebugLogged=!0),push(),this._mirror?(translate(n,0),scale(-1,1),image(this._video,e.x,e.y,e.width,e.height)):image(this._video,e.x,e.y,e.width,e.height),pop()}}function createPhoneCamera(e="user",n=!0,o="fitHeight"){return new PhoneCamera(e,n,o)}function enableCameraButton(e="ENABLE CAMERA",n="Starting camera..."){_createPermissionButton(e,n,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via button")})}function enableCameraTap(e="Tap screen to enable camera"){navigator.permissions&&navigator.permissions.query?navigator.permissions.query({name:"camera"}).then(n=>{"granted"===n.state?(console.log("✅ Camera permission already granted - auto-starting"),_requestCameraPermission()):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}).catch(()=>{_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}async function _requestCameraPermission(){try{if(void 0!==window._phoneCameras&&Array.isArray(window._phoneCameras))for(let e of window._phoneCameras)!e||e._ready||e._video||e._initializeCamera();"function"==typeof userCameraReady&&userCameraReady(),_notifySketchReady()}catch(e){console.error("Camera permission error:",e),_debugVisible&&debugError("Camera permission error:",e),_notifySketchReady()}}if(window.createPhoneCamera=createPhoneCamera,window.enableCameraButton=enableCameraButton,window.enableCameraTap=enableCameraTap,"undefined"!=typeof p5&&p5.prototype){const e=p5.prototype.image;p5.prototype.image=function(...n){if(n[0]instanceof PhoneCamera){n[0]._draw()}else e.apply(this,n)}}"undefined"!=typeof p5&&p5.prototype&&"function"!=typeof p5.registerAddon&&(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.enableSpeechTap=enableSpeechTap,p5.prototype.enableSpeechButton=enableSpeechButton,p5.prototype.enableVibrationTap=enableVibrationTap,p5.prototype.enableVibrationButton=enableVibrationButton,p5.prototype.vibrate=vibrate,p5.prototype.stopVibration=stopVibration,p5.prototype.enableNfcTap=enableNfcTap,p5.prototype.enableNfcButton=enableNfcButton,p5.prototype.stopNfc=stopNfc,p5.prototype.setNfcTagAlias=setNfcTagAlias,p5.prototype.getNfcTagAlias=getNfcTagAlias,p5.prototype.isNfcTag=isNfcTag,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,p5.prototype.enableGyroCanvas=enableGyroCanvas,p5.prototype.enableMicCanvas=enableMicCanvas,p5.prototype.enableSoundCanvas=enableSoundCanvas,p5.prototype.enableSpeechCanvas=enableSpeechCanvas,p5.prototype.enableVibrationCanvas=enableVibrationCanvas,p5.prototype.enableNfcCanvas=enableNfcCanvas,p5.prototype.enableAllCanvas=enableAllCanvas,p5.prototype.enableCameraCanvas=enableCameraCanvas,p5.prototype.enableGyroBanner=enableGyroBanner,p5.prototype.enableMicBanner=enableMicBanner,p5.prototype.enableSoundBanner=enableSoundBanner,p5.prototype.enableSpeechBanner=enableSpeechBanner,p5.prototype.enableVibrationBanner=enableVibrationBanner,p5.prototype.enableNfcBanner=enableNfcBanner,p5.prototype.enableAllBanner=enableAllBanner,p5.prototype.enableCameraBanner=enableCameraBanner,p5.prototype.enableGyroOn=enableGyroOn,p5.prototype.enableMicOn=enableMicOn,p5.prototype.enableSoundOn=enableSoundOn,p5.prototype.enableSpeechOn=enableSpeechOn,p5.prototype.enableVibrationOn=enableVibrationOn,p5.prototype.enableNfcOn=enableNfcOn,p5.prototype.enableAllOn=enableAllOn,p5.prototype.enableCameraOn=enableCameraOn,p5.prototype.createPhoneCamera=createPhoneCamera,p5.prototype.enableCameraButton=enableCameraButton,p5.prototype.enableCameraTap=enableCameraTap,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")),"undefined"!=typeof p5&&"function"==typeof p5.registerAddon&&p5.registerAddon(function(e,n,o){o.presetup=function(){this.lockGestures=lockGestures,this.enableGyroTap=enableGyroTap,this.enableGyroButton=enableGyroButton,this.enableMicTap=enableMicTap,this.enableMicButton=enableMicButton,this.enableSoundTap=enableSoundTap,this.enableSoundButton=enableSoundButton,this.enableSpeechTap=enableSpeechTap,this.enableSpeechButton=enableSpeechButton,this.enableVibrationTap=enableVibrationTap,this.enableVibrationButton=enableVibrationButton,this.vibrate=vibrate,this.stopVibration=stopVibration,this.enableNfcTap=enableNfcTap,this.enableNfcButton=enableNfcButton,this.stopNfc=stopNfc,this.setNfcTagAlias=setNfcTagAlias,this.getNfcTagAlias=getNfcTagAlias,this.isNfcTag=isNfcTag,this.enableAllTap=enableAllTap,this.enableAllButton=enableAllButton,this.enableGyroCanvas=enableGyroCanvas,this.enableMicCanvas=enableMicCanvas,this.enableSoundCanvas=enableSoundCanvas,this.enableSpeechCanvas=enableSpeechCanvas,this.enableVibrationCanvas=enableVibrationCanvas,this.enableNfcCanvas=enableNfcCanvas,this.enableAllCanvas=enableAllCanvas,this.enableCameraCanvas=enableCameraCanvas,this.enableGyroBanner=enableGyroBanner,this.enableMicBanner=enableMicBanner,this.enableSoundBanner=enableSoundBanner,this.enableSpeechBanner=enableSpeechBanner,this.enableVibrationBanner=enableVibrationBanner,this.enableNfcBanner=enableNfcBanner,this.enableAllBanner=enableAllBanner,this.enableCameraBanner=enableCameraBanner,this.enableGyroOn=enableGyroOn,this.enableMicOn=enableMicOn,this.enableSoundOn=enableSoundOn,this.enableSpeechOn=enableSpeechOn,this.enableVibrationOn=enableVibrationOn,this.enableNfcOn=enableNfcOn,this.enableAllOn=enableAllOn,this.enableCameraOn=enableCameraOn,this.createPhoneCamera=createPhoneCamera,this.enableCameraButton=enableCameraButton,this.enableCameraTap=enableCameraTap,this.showDebug=showDebug,this.hideDebug=hideDebug,this.toggleDebug=toggleDebug,this.debug=debug,this.debugError=debugError,this.debugWarn=debugWarn},console.log("✅ Mobile p5.js Permissions: registered as p5.js 2.0 addon")});
|
|
@@ -2,8 +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@
|
|
6
|
-
<script src="https://
|
|
5
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
6
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.sound@0.3.0/dist/p5.sound.min.js"></script>
|
|
7
8
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.min.js"></script>
|
|
8
9
|
<style>
|
|
9
10
|
body {
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title>P5.js Mobile - Speech Recognition</title>
|
|
5
|
-
<script src="https://cdn.jsdelivr.net/npm/p5@
|
|
6
|
-
<script src="https://
|
|
5
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
6
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.sound@0.3.0/dist/p5.sound.min.js"></script>
|
|
7
8
|
<script src="https://cdn.jsdelivr.net/gh/IDMNYU/p5.js-speech@0.0.3/lib/p5.speech.js"></script>
|
|
8
9
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.min.js"></script>
|
|
9
10
|
<style>
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
</style>
|
|
16
16
|
|
|
17
17
|
<!-- Load p5.js library -->
|
|
18
|
-
<script src="https://
|
|
19
|
-
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
20
20
|
|
|
21
21
|
<!-- Load the mobile p5.js permissions library -->
|
|
22
22
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.js"></script>
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
</style>
|
|
16
16
|
|
|
17
17
|
<!-- Load p5.js library -->
|
|
18
|
-
<script src="https://
|
|
19
|
-
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
20
20
|
|
|
21
21
|
<!-- Load the mobile p5.js permissions library -->
|
|
22
22
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.js"></script>
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
</style>
|
|
16
16
|
|
|
17
17
|
<!-- Load p5.js library -->
|
|
18
|
-
<script src="https://
|
|
19
|
-
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
20
20
|
|
|
21
21
|
<!-- Load the mobile p5.js permissions library -->
|
|
22
22
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.js"></script>
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
}
|
|
14
14
|
</style>
|
|
15
15
|
|
|
16
|
-
<script src="https://
|
|
17
|
-
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
18
18
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.js"></script>
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
}
|
|
14
14
|
</style>
|
|
15
15
|
|
|
16
|
-
<script src="https://
|
|
17
|
-
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
18
18
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.js"></script>
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
}
|
|
14
14
|
</style>
|
|
15
15
|
|
|
16
|
-
<script src="https://
|
|
17
|
-
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
18
18
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.js"></script>
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
</style>
|
|
16
16
|
|
|
17
17
|
<!-- Load p5.js library -->
|
|
18
|
-
<script src="https://
|
|
19
|
-
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
20
20
|
|
|
21
21
|
<!-- Load the local p5-phone source so this example uses the current repo code -->
|
|
22
|
-
<script src="
|
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.min.js"></script>
|
|
23
23
|
|
|
24
24
|
</head>
|
|
25
25
|
<body>
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
}
|
|
14
14
|
</style>
|
|
15
15
|
|
|
16
|
-
<script src="https://
|
|
17
|
-
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
18
18
|
|
|
19
19
|
<!-- Load the local p5-phone source so this example uses the current repo code -->
|
|
20
|
-
<script src="
|
|
20
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.min.js"></script>
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<script src="sketch.js"></script>
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
</style>
|
|
16
16
|
|
|
17
17
|
<!-- Load p5.js library -->
|
|
18
|
-
<script src="https://
|
|
19
|
-
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.js"></script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.js-compatibility@0.2.0/src/preload.js"></script>
|
|
20
20
|
|
|
21
21
|
<!-- Load p5.sound library for audio -->
|
|
22
|
-
<script src="https://
|
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/p5.sound@0.3.0/dist/p5.sound.min.js"></script>
|
|
23
23
|
|
|
24
24
|
<!-- Load the mobile p5.js permissions library -->
|
|
25
25
|
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.1/dist/p5-phone.min.js"></script>
|