p5-phone 1.6.3 → 1.6.4
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 +3 -3
- package/dist/p5-phone.js +37 -1
- package/dist/p5-phone.min.js +2 -2
- package/examples/Phone Sensor Examples/microphone/01_mic_level/index.html +1 -1
- package/examples/Phone Sensor Examples/microphone/02_speech_recognition/README.md +135 -0
- package/examples/Phone Sensor Examples/microphone/02_speech_recognition/index.html +20 -0
- package/examples/Phone Sensor Examples/microphone/02_speech_recognition/sketch.js +91 -0
- package/examples/Phone Sensor Examples/movement/01_orientation_basic/index.html +1 -1
- package/examples/Phone Sensor Examples/movement/02_rotational_velocity/index.html +1 -1
- package/examples/Phone Sensor Examples/movement/03_acceleration/index.html +1 -1
- package/examples/Phone Sensor Examples/sound/01_dual_audio/index.html +1 -1
- package/examples/Phone Sensor Examples/sound/02_volume_touches/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/01_touch_basic/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/02_touch_zones/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/03_touch_count/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/04_touch_distance/index.html +1 -1
- package/examples/Phone Sensor Examples/touch/05_touch_angle/index.html +1 -1
- package/examples/Phone Sensor Examples/vibration/01_haptic_feedback/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/index.html +1 -1
- package/examples/Phone Sensor Examples - Minimal/vibration/01_haptic_feedback/index.html +1 -1
- package/examples/Phone and Gif/collision/index.html +1 -1
- package/examples/Phone and Gif/fetch/index.html +1 -1
- package/examples/Phone and Gif/fly/index.html +1 -1
- package/examples/Phone and Gif/roll/index.html +1 -1
- package/examples/UXcompare/button-vs-movement/index.html +1 -1
- package/examples/UXcompare/button-vs-orientation/index.html +1 -1
- package/examples/UXcompare/button-vs-shake/index.html +1 -1
- package/examples/UXcompare/gyroscope-demo/index.html +1 -1
- package/examples/UXcompare/microphone-demo/index.html +1 -1
- package/examples/UXcompare/slider-vs-angle/index.html +1 -1
- package/examples/UXcompare/slider-vs-distance/index.html +1 -1
- package/examples/UXcompare/slider-vs-microphone/index.html +1 -1
- package/examples/UXcompare/slider-vs-touches/index.html +1 -1
- package/examples/UXcompare/sliders-vs-acceleration/index.html +1 -1
- package/examples/UXcompare/sliders-vs-rotation/index.html +1 -1
- package/examples/blankTemplate/index.html +1 -1
- package/examples/homepage/index.html +173 -5
- package/examples/ml5/Gaze_detector_class/GazeDetector.js +452 -0
- package/examples/ml5/Gaze_detector_class/README.md +592 -0
- package/examples/ml5/Gaze_detector_class/index.html +37 -0
- package/examples/ml5/Gaze_detector_class/sketch.js +220 -0
- package/examples/ml5/PHONE_BodyPose_two_points/index.html +35 -0
- package/examples/ml5/PHONE_BodyPose_two_points/sketch.js +390 -0
- package/examples/ml5/PHONE_FaceMesh_two_points/index.html +1 -1
- package/examples/ml5/PHONE_HandPose_two_points/index.html +35 -0
- package/examples/ml5/PHONE_HandPose_two_points/sketch.js +380 -0
- package/examples/ml5/THREE_BodyPose_two_points/functions.js +526 -0
- package/examples/ml5/THREE_BodyPose_two_points/index.html +49 -0
- package/examples/ml5/THREE_BodyPose_two_points/sketch.js +354 -0
- package/examples/ml5/THREE_FaceMesh_two_points/functions.js +532 -0
- package/examples/ml5/THREE_FaceMesh_two_points/index.html +49 -0
- package/examples/ml5/THREE_FaceMesh_two_points/sketch.js +344 -0
- package/examples/ml5/THREE_HandPose_two_points/functions.js +467 -0
- package/examples/ml5/THREE_HandPose_two_points/index.html +101 -0
- package/examples/ml5/THREE_HandPose_two_points/sketch.js +332 -0
- package/package.json +1 -1
- package/src/p5-phone.js +37 -1
package/README.md
CHANGED
|
@@ -67,10 +67,10 @@ This library simplifies access to the following p5.js mobile sensor and audio co
|
|
|
67
67
|
|
|
68
68
|
```html
|
|
69
69
|
<!-- Minified version (recommended) -->
|
|
70
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
70
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
71
71
|
|
|
72
72
|
<!-- Development version (larger, with comments) -->
|
|
73
|
-
<!-- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
73
|
+
<!-- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.js"></script> -->
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
### Basic Setup
|
|
@@ -98,7 +98,7 @@ This library simplifies access to the following p5.js mobile sensor and audio co
|
|
|
98
98
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
99
99
|
|
|
100
100
|
<!-- Load p5-phone library -->
|
|
101
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
101
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
102
102
|
|
|
103
103
|
</head>
|
|
104
104
|
<body>
|
package/dist/p5-phone.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.6.
|
|
2
|
+
* p5-phone v1.6.4
|
|
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
|
*
|
|
@@ -177,6 +177,20 @@ function enableSoundTap(message = 'Tap screen to enable sound') {
|
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Enable speech recognition with tap-to-start
|
|
182
|
+
* User taps anywhere on screen to enable
|
|
183
|
+
* IMPORTANT: This does NOT create a p5.AudioIn object
|
|
184
|
+
* Only activates audio context for Web Speech API
|
|
185
|
+
* You must create your own p5.SpeechRec object after this
|
|
186
|
+
*/
|
|
187
|
+
function enableSpeechTap(message = 'Tap to enable speech recognition') {
|
|
188
|
+
_createTapToEnable(message, async () => {
|
|
189
|
+
await _requestSpeechPermission();
|
|
190
|
+
console.log('✅ Speech recognition enabled via tap');
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
180
194
|
/**
|
|
181
195
|
* Enable vibration motor with a button interface
|
|
182
196
|
* Creates a start button that user must click
|
|
@@ -339,6 +353,26 @@ async function _requestSoundOutput() {
|
|
|
339
353
|
}
|
|
340
354
|
}
|
|
341
355
|
|
|
356
|
+
async function _requestSpeechPermission() {
|
|
357
|
+
try {
|
|
358
|
+
// Start audio context for Web Speech API
|
|
359
|
+
// DO NOT create or start p5.AudioIn - this would conflict with speech recognition
|
|
360
|
+
if (typeof userStartAudio !== 'undefined') {
|
|
361
|
+
await userStartAudio();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
window.speechEnabled = true;
|
|
365
|
+
_notifySketchReady();
|
|
366
|
+
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error('Speech permission error:', error);
|
|
369
|
+
if (_debugVisible) {
|
|
370
|
+
debugError('Speech permission error:', error);
|
|
371
|
+
}
|
|
372
|
+
_notifySketchReady();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
342
376
|
async function _requestVibrationPermission() {
|
|
343
377
|
try {
|
|
344
378
|
// Check if Vibration API is supported
|
|
@@ -930,6 +964,7 @@ window.enableMicTap = enableMicTap;
|
|
|
930
964
|
window.enableMicButton = enableMicButton;
|
|
931
965
|
window.enableSoundTap = enableSoundTap;
|
|
932
966
|
window.enableSoundButton = enableSoundButton;
|
|
967
|
+
window.enableSpeechTap = enableSpeechTap;
|
|
933
968
|
window.enableVibrationTap = enableVibrationTap;
|
|
934
969
|
window.enableVibrationButton = enableVibrationButton;
|
|
935
970
|
window.vibrate = vibrate;
|
|
@@ -1667,6 +1702,7 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
|
|
|
1667
1702
|
p5.prototype.enableMicButton = enableMicButton;
|
|
1668
1703
|
p5.prototype.enableSoundTap = enableSoundTap;
|
|
1669
1704
|
p5.prototype.enableSoundButton = enableSoundButton;
|
|
1705
|
+
p5.prototype.enableSpeechTap = enableSpeechTap;
|
|
1670
1706
|
p5.prototype.enableVibrationTap = enableVibrationTap;
|
|
1671
1707
|
p5.prototype.enableVibrationButton = enableVibrationButton;
|
|
1672
1708
|
p5.prototype.vibrate = vibrate;
|
package/dist/p5-phone.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.6.
|
|
2
|
+
* p5-phone v1.6.4
|
|
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;let _permissionsInitialized=!1,_micInstance=null;function lockGestures(){window.gesturesLocked||(console.log("🔒 Locking mobile gestures..."),_initializeGestureBlocking(),_initializeP5TouchOverrides(),window.gesturesLocked=!0,console.log("✅ Mobile gestures locked"))}function enableGyroButton(e="ENABLE MOTION SENSORS",n="Requesting motion sensors..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via button")})}function enableGyroTap(e="Tap screen to enable motion sensors"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via tap")})}function enableMicButton(e="ENABLE MICROPHONE",n="Requesting microphone access..."){_createPermissionButton(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via button")})}function enableMicTap(e="Tap screen to enable microphone"){_createTapToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via tap")})}function enableSoundButton(e="ENABLE SOUND",n="Enabling audio..."){_createPermissionButton(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via button")})}function enableSoundTap(e="Tap screen to enable sound"){_createTapToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via tap")})}function enableVibrationButton(e="ENABLE VIBRATION",n="Enabling vibration..."){_createPermissionButton(e,n,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via button")})}function enableVibrationTap(e="Tap screen to enable vibration"){_createTapToEnable(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via tap")})}function vibrate(e){return window.vibrationEnabled?navigator.vibrate?navigator.vibrate(e):(console.warn("⚠️ Vibration API not supported on this device"),!1):(console.warn("⚠️ Vibration not enabled. Call enableVibrationTap() or enableVibrationButton() first."),!1)}function stopVibration(){navigator.vibrate&&navigator.vibrate(0)}async function _requestMotionPermissions(){try{if("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission){const e=await DeviceOrientationEvent.requestPermission();if(console.log("Orientation permission:",e),"undefined"!=typeof DeviceMotionEvent&&"function"==typeof DeviceMotionEvent.requestPermission){const e=await DeviceMotionEvent.requestPermission();console.log("Motion permission:",e)}}window.sensorsEnabled=!0,_notifySketchReady()}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0,_notifySketchReady()}}async function _requestMicrophonePermissions(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),"undefined"!=typeof mic&&mic&&mic.start?(mic.start(),_micInstance=mic,window.micEnabled=!0):console.warn("No microphone object found. Create one with: mic = new p5.AudioIn();"),_notifySketchReady()}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e),_notifySketchReady()}}async function _requestSoundOutput(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0,_notifySketchReady()}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0,_notifySketchReady()}}async function _requestVibrationPermission(){try{if(!navigator.vibrate)return console.warn("⚠️ Vibration API not supported on this device (likely iOS)"),_debugVisible&&debugWarn("Vibration API not supported on this device"),window.vibrationEnabled=!1,void _notifySketchReady();navigator.vibrate(1)?(window.vibrationEnabled=!0,console.log("✅ Vibration enabled")):(console.warn("⚠️ Vibration API available but vibration failed"),window.vibrationEnabled=!1),_notifySketchReady()}catch(e){console.error("Vibration permission error:",e),_debugVisible&&debugError("Vibration permission error:",e),window.vibrationEnabled=!1,_notifySketchReady()}}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,vibration:window.vibrationEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,t){_removeExistingUI();const o=document.createElement("button");o.id="permissionButton",o.textContent=e,o.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 ",o.addEventListener("mouseenter",()=>{o.style.transform="translate(-50%, -50%) scale(1.05)"}),o.addEventListener("mouseleave",()=>{o.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{o.parentNode&&(o.style.display="none",i.style.display="block",await t(),i.style.display="none",_removeExistingUI())};o.addEventListener("click",r),o.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),o.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(o),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();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 o=document.createElement("div");o.textContent=e,o.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(o);const i=async()=>{t.parentNode&&(o.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"),t=document.getElementById("tapOverlay");e&&e.remove(),n&&n.remove(),t&&t.remove()}function _initializeGestureBlocking(){window.history.pushState(null,"",window.location.href),window.onpopstate=function(){window.history.pushState(null,"",window.location.href)},window.addEventListener("beforeunload",function(e){e.preventDefault(),e.returnValue=""}),_initializeEdgeSwipePrevention(),_initializeOtherGesturePrevention()}function _initializeEdgeSwipePrevention(){let e=0,n=0;document.addEventListener("touchstart",function(t){t.touches&&t.touches.length>0&&(e=t.touches[0].clientX,n=t.touches[0].clientY,(e<20||e>window.innerWidth-20)&&t.preventDefault())},{passive:!1,capture:!0}),document.addEventListener("touchmove",function(t){if(!t.touches||0===t.touches.length)return;let o=t.touches[0].clientX,i=t.touches[0].clientY,r=o-e,a=i-n;(e<20&&r>0||e>window.innerWidth-20&&r<0)&&(t.preventDefault(),t.stopPropagation()),0===window.pageYOffset&&a>0&&t.preventDefault(),!t.target||"CANVAS"!==t.target.tagName||document.getElementById("tapOverlay")||document.getElementById("permissionButton")||t.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 t=Date.now();t-e<=300&&n.preventDefault(),e=t},!1),window.oncontextmenu=function(e){return e.preventDefault(),e.stopPropagation(),!1}}function _initializeP5TouchOverrides(){setTimeout(()=>{if(window._setupDone)_overrideP5Touch();else{const e=setInterval(()=>{window._setupDone&&(_overrideP5Touch(),clearInterval(e))},100)}},100)}function _overrideP5Touch(){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},t=window.touchEnded||function(){},o=window.mousePressed||function(){},i=window.mouseDragged||function(){},r=window.mouseReleased||function(){};window.touchStarted=function(n){return e(n),!1},window.touchMoved=function(e){return n(e),!1},window.touchEnded=function(e){return t(e),!1},window.mousePressed=function(e){return o(e),!1},window.mouseDragged=function(e){return i(e),!1},window.mouseReleased=function(e){return r(e),!1}}document.addEventListener("DOMContentLoaded",function(){const e=document.getElementById("startButton"),n=document.getElementById("statusText");e&&n&&(console.warn("⚠️ Legacy HTML elements detected. Consider using the new API functions instead."),e.addEventListener("click",async()=>{e.classList.add("hidden"),n.classList.remove("hidden"),n.textContent="Requesting permissions...",await _requestMotionPermissions(),await _requestMicrophonePermissions(),n.classList.add("hidden")}),lockGestures())});let _debugPanel=null,_debugVisible=!1,_debugMessages=[];const MAX_DEBUG_MESSAGES=20;function showDebug(){_createDebugPanel(),_debugPanel.style.display="block",_debugVisible=!0,window._debugVisible=!0,_setupConsoleOverrides(),_displayEarlyErrors()}function hideDebug(){_debugPanel&&(_debugPanel.style.display="none",_debugVisible=!1)}function toggleDebug(){_debugVisible?hideDebug():showDebug()}function debug(...e){console.log(...e);_addDebugMessage(e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" "),"log")}function debugError(...e){(window._originalConsoleError||console.error).apply(console,e);_addDebugMessage(`❌ ERROR: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"error")}function debugWarn(...e){(window._originalConsoleWarn||console.warn).apply(console,e);_addDebugMessage(`⚠️ WARNING: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"warning")}function _addDebugMessage(e,n="log"){const t={text:`[${(new Date).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}] ${e}`,type:n};_debugMessages.push(t),_debugMessages.length>20&&_debugMessages.shift(),_debugPanel&&_updateDebugDisplay()}function _setupConsoleOverrides(){window._consoleOverrideSet||(window._consoleOverrideSet=!0,window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,console.error=function(...e){window._originalConsoleError.apply(console,e),_debugVisible&&debugError(...e)},console.warn=function(...e){window._originalConsoleWarn.apply(console,e),_debugVisible&&debugWarn(...e)})}function _displayEarlyErrors(){window._earlyErrors&&window._earlyErrors.length>0&&(debugError(`🚨 Found ${window._earlyErrors.length} early error(s):`),window._earlyErrors.forEach(e=>{debugError(e.message),e.stack&&debugError("Stack trace:",e.stack)}),window._earlyErrors=[])}function _createDebugPanel(){if(_debugPanel)return;_debugPanel=document.createElement("div"),_debugPanel.id="mobile-debug-panel",_debugPanel.innerHTML='\n <div id="mobile-debug-header">\n <span>Debug</span>\n <button id="mobile-debug-close">×</button>\n </div>\n <div id="mobile-debug-content"></div>\n ';const e=document.createElement("style");e.textContent="\n #mobile-debug-panel {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 350px;\n max-width: calc(100vw - 40px);\n max-height: 400px;\n background: rgba(0, 0, 0, 0.9);\n color: #ffffff;\n font-family: 'Courier New', monospace;\n font-size: 12px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 10000;\n display: none;\n }\n \n #mobile-debug-header {\n background: rgba(255, 255, 255, 0.1);\n padding: 8px 12px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-radius: 8px 8px 0 0;\n }\n \n #mobile-debug-header span {\n font-weight: bold;\n font-size: 13px;\n }\n \n #mobile-debug-close {\n background: none;\n border: none;\n color: #ffffff;\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n #mobile-debug-close:hover {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n }\n \n #mobile-debug-content {\n padding: 12px;\n max-height: 340px;\n overflow-y: auto;\n word-wrap: break-word;\n line-height: 1.4;\n }\n \n .debug-message {\n margin-bottom: 4px;\n white-space: pre-wrap;\n }\n \n .debug-message.error {\n color: #ff6b6b;\n background: rgba(255, 107, 107, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ff6b6b;\n }\n \n .debug-message.warning {\n color: #ffd93d;\n background: rgba(255, 217, 61, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ffd93d;\n }\n \n .debug-timestamp {\n color: #888;\n font-size: 10px;\n }\n \n @media (max-width: 480px) {\n #mobile-debug-panel {\n width: calc(100vw - 20px);\n right: 10px;\n top: 10px;\n }\n }\n ",document.head.appendChild(e),document.body.appendChild(_debugPanel),document.getElementById("mobile-debug-close").onclick=hideDebug,_updateDebugDisplay()}function _updateDebugDisplay(){if(!_debugPanel)return;const e=document.getElementById("mobile-debug-content");e&&(e.innerHTML=_debugMessages.map(e=>"string"==typeof e?`<div class="debug-message">${e}</div>`:`<div class="debug-message ${e.type}">${e.text}</div>`).join(""),e.scrollTop=e.scrollHeight)}debug.clear=function(){_debugMessages=[],_debugPanel&&_updateDebugDisplay(),console.clear()},window.debug=debug,window.debugError=debugError,window.debugWarn=debugWarn,window.showDebug=showDebug,window.hideDebug=hideDebug,window.toggleDebug=toggleDebug,window.lockGestures=lockGestures,window.enableGyroTap=enableGyroTap,window.enableGyroButton=enableGyroButton,window.enableMicTap=enableMicTap,window.enableMicButton=enableMicButton,window.enableSoundTap=enableSoundTap,window.enableSoundButton=enableSoundButton,window.enableVibrationTap=enableVibrationTap,window.enableVibrationButton=enableVibrationButton,window.vibrate=vibrate,window.stopVibration=stopVibration,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton;class PhoneCamera{constructor(e="user",n=!0,t="fitHeight"){this._active=e,this._mirror=n,this._mode=t,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(){if(this._video&&this._video.elt&&this._video.elt.readyState>=2){if(this._onReadyCallback){const e=this._onReadyCallback;this._onReadyCallback=null,e()}}else setTimeout(()=>this._checkVideoReady(),100)}_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,t="undefined"!=typeof width?width:window.innerWidth,o="undefined"!=typeof height?height:window.innerHeight;let i,r,a,s;if("fixed"===this._mode)i=this._fixedWidth,r=this._fixedHeight,a=(t-i)/2,s=(o-r)/2;else if("fitWidth"===this._mode)i=t,r=n/e*i,a=0,s=(o-r)/2;else if("fitHeight"===this._mode)r=o,i=e/n*r,a=(t-i)/2,s=0;else if("cover"===this._mode){const d=Math.max(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}else if("contain"===this._mode){const d=Math.min(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}return{x:a,y:s,width:i,height:r,scaleX:i/e,scaleY:r/n}}mapPoint(e,n){const t=this.getDimensions();let o=e*t.scaleX;const i=n*t.scaleY;this._mirror&&(o=t.width-o);return{x:o+t.x,y:i+t.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)}_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,t="fitHeight"){return new PhoneCamera(e,n,t)}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.enableVibrationTap=enableVibrationTap,p5.prototype.enableVibrationButton=enableVibrationButton,p5.prototype.vibrate=vibrate,p5.prototype.stopVibration=stopVibration,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,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"));
|
|
10
|
+
window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,window._debugErrorHandlersSet||(window._debugErrorHandlersSet=!0,window._earlyErrors=window._earlyErrors||[],window.addEventListener("error",function(e){const n=`${e.error?.message||e.message||"Unknown error"} (${e.filename?e.filename.split("/").pop():"unknown file"}:${e.lineno||"unknown line"})`;console.error("🚨 Error caught:",n),e.error?.stack&&console.error("Stack:",e.error.stack),window._earlyErrors.push({type:"error",message:"JavaScript Error: "+n,stack:e.error?.stack}),!1===window.SHOW_DEBUG||window._debugVisible||"function"==typeof showDebug&&showDebug(),window._debugVisible&&"function"==typeof debugError&&(debugError("JavaScript Error:",n),e.error?.stack&&debugError("Stack trace:",e.error.stack))}),window.addEventListener("unhandledrejection",function(e){const n=e.reason?.message||e.reason||"Unknown promise rejection";console.error("🚨 Promise rejection caught:",n),window._earlyErrors.push({type:"error",message:"Unhandled Promise Rejection: "+n}),window._debugVisible&&"function"==typeof debugError&&debugError("Unhandled Promise Rejection:",n)})),window.sensorsEnabled=!1,window.micEnabled=!1,window.soundEnabled=!1,window.gesturesLocked=!1,window.vibrationEnabled=!1;let _permissionsInitialized=!1,_micInstance=null;function lockGestures(){window.gesturesLocked||(console.log("🔒 Locking mobile gestures..."),_initializeGestureBlocking(),_initializeP5TouchOverrides(),window.gesturesLocked=!0,console.log("✅ Mobile gestures locked"))}function enableGyroButton(e="ENABLE MOTION SENSORS",n="Requesting motion sensors..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via button")})}function enableGyroTap(e="Tap screen to enable motion sensors"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via tap")})}function enableMicButton(e="ENABLE MICROPHONE",n="Requesting microphone access..."){_createPermissionButton(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via button")})}function enableMicTap(e="Tap screen to enable microphone"){_createTapToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via tap")})}function enableSoundButton(e="ENABLE SOUND",n="Enabling audio..."){_createPermissionButton(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via button")})}function enableSoundTap(e="Tap screen to enable sound"){_createTapToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via tap")})}function enableSpeechTap(e="Tap to enable speech recognition"){_createTapToEnable(e,async()=>{await _requestSpeechPermission(),console.log("✅ Speech recognition enabled via tap")})}function enableVibrationButton(e="ENABLE VIBRATION",n="Enabling vibration..."){_createPermissionButton(e,n,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via button")})}function enableVibrationTap(e="Tap screen to enable vibration"){_createTapToEnable(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via tap")})}function vibrate(e){return window.vibrationEnabled?navigator.vibrate?navigator.vibrate(e):(console.warn("⚠️ Vibration API not supported on this device"),!1):(console.warn("⚠️ Vibration not enabled. Call enableVibrationTap() or enableVibrationButton() first."),!1)}function stopVibration(){navigator.vibrate&&navigator.vibrate(0)}async function _requestMotionPermissions(){try{if("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission){const e=await DeviceOrientationEvent.requestPermission();if(console.log("Orientation permission:",e),"undefined"!=typeof DeviceMotionEvent&&"function"==typeof DeviceMotionEvent.requestPermission){const e=await DeviceMotionEvent.requestPermission();console.log("Motion permission:",e)}}window.sensorsEnabled=!0,_notifySketchReady()}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0,_notifySketchReady()}}async function _requestMicrophonePermissions(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),"undefined"!=typeof mic&&mic&&mic.start?(mic.start(),_micInstance=mic,window.micEnabled=!0):console.warn("No microphone object found. Create one with: mic = new p5.AudioIn();"),_notifySketchReady()}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e),_notifySketchReady()}}async function _requestSoundOutput(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0,_notifySketchReady()}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0,_notifySketchReady()}}async function _requestSpeechPermission(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.speechEnabled=!0,_notifySketchReady()}catch(e){console.error("Speech permission error:",e),_debugVisible&&debugError("Speech permission error:",e),_notifySketchReady()}}async function _requestVibrationPermission(){try{if(!navigator.vibrate)return console.warn("⚠️ Vibration API not supported on this device (likely iOS)"),_debugVisible&&debugWarn("Vibration API not supported on this device"),window.vibrationEnabled=!1,void _notifySketchReady();navigator.vibrate(1)?(window.vibrationEnabled=!0,console.log("✅ Vibration enabled")):(console.warn("⚠️ Vibration API available but vibration failed"),window.vibrationEnabled=!1),_notifySketchReady()}catch(e){console.error("Vibration permission error:",e),_debugVisible&&debugError("Vibration permission error:",e),window.vibrationEnabled=!1,_notifySketchReady()}}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,vibration:window.vibrationEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,t){_removeExistingUI();const o=document.createElement("button");o.id="permissionButton",o.textContent=e,o.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 ",o.addEventListener("mouseenter",()=>{o.style.transform="translate(-50%, -50%) scale(1.05)"}),o.addEventListener("mouseleave",()=>{o.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{o.parentNode&&(o.style.display="none",i.style.display="block",await t(),i.style.display="none",_removeExistingUI())};o.addEventListener("click",r),o.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),o.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(o),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();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 o=document.createElement("div");o.textContent=e,o.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(o);const i=async()=>{t.parentNode&&(o.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"),t=document.getElementById("tapOverlay");e&&e.remove(),n&&n.remove(),t&&t.remove()}function _initializeGestureBlocking(){window.history.pushState(null,"",window.location.href),window.onpopstate=function(){window.history.pushState(null,"",window.location.href)},window.addEventListener("beforeunload",function(e){e.preventDefault(),e.returnValue=""}),_initializeEdgeSwipePrevention(),_initializeOtherGesturePrevention()}function _initializeEdgeSwipePrevention(){let e=0,n=0;document.addEventListener("touchstart",function(t){t.touches&&t.touches.length>0&&(e=t.touches[0].clientX,n=t.touches[0].clientY,(e<20||e>window.innerWidth-20)&&t.preventDefault())},{passive:!1,capture:!0}),document.addEventListener("touchmove",function(t){if(!t.touches||0===t.touches.length)return;let o=t.touches[0].clientX,i=t.touches[0].clientY,r=o-e,a=i-n;(e<20&&r>0||e>window.innerWidth-20&&r<0)&&(t.preventDefault(),t.stopPropagation()),0===window.pageYOffset&&a>0&&t.preventDefault(),!t.target||"CANVAS"!==t.target.tagName||document.getElementById("tapOverlay")||document.getElementById("permissionButton")||t.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 t=Date.now();t-e<=300&&n.preventDefault(),e=t},!1),window.oncontextmenu=function(e){return e.preventDefault(),e.stopPropagation(),!1}}function _initializeP5TouchOverrides(){setTimeout(()=>{if(window._setupDone)_overrideP5Touch();else{const e=setInterval(()=>{window._setupDone&&(_overrideP5Touch(),clearInterval(e))},100)}},100)}function _overrideP5Touch(){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},t=window.touchEnded||function(){},o=window.mousePressed||function(){},i=window.mouseDragged||function(){},r=window.mouseReleased||function(){};window.touchStarted=function(n){return e(n),!1},window.touchMoved=function(e){return n(e),!1},window.touchEnded=function(e){return t(e),!1},window.mousePressed=function(e){return o(e),!1},window.mouseDragged=function(e){return i(e),!1},window.mouseReleased=function(e){return r(e),!1}}document.addEventListener("DOMContentLoaded",function(){const e=document.getElementById("startButton"),n=document.getElementById("statusText");e&&n&&(console.warn("⚠️ Legacy HTML elements detected. Consider using the new API functions instead."),e.addEventListener("click",async()=>{e.classList.add("hidden"),n.classList.remove("hidden"),n.textContent="Requesting permissions...",await _requestMotionPermissions(),await _requestMicrophonePermissions(),n.classList.add("hidden")}),lockGestures())});let _debugPanel=null,_debugVisible=!1,_debugMessages=[];const MAX_DEBUG_MESSAGES=20;function showDebug(){_createDebugPanel(),_debugPanel.style.display="block",_debugVisible=!0,window._debugVisible=!0,_setupConsoleOverrides(),_displayEarlyErrors()}function hideDebug(){_debugPanel&&(_debugPanel.style.display="none",_debugVisible=!1)}function toggleDebug(){_debugVisible?hideDebug():showDebug()}function debug(...e){console.log(...e);_addDebugMessage(e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" "),"log")}function debugError(...e){(window._originalConsoleError||console.error).apply(console,e);_addDebugMessage(`❌ ERROR: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"error")}function debugWarn(...e){(window._originalConsoleWarn||console.warn).apply(console,e);_addDebugMessage(`⚠️ WARNING: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"warning")}function _addDebugMessage(e,n="log"){const t={text:`[${(new Date).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}] ${e}`,type:n};_debugMessages.push(t),_debugMessages.length>20&&_debugMessages.shift(),_debugPanel&&_updateDebugDisplay()}function _setupConsoleOverrides(){window._consoleOverrideSet||(window._consoleOverrideSet=!0,window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,console.error=function(...e){window._originalConsoleError.apply(console,e),_debugVisible&&debugError(...e)},console.warn=function(...e){window._originalConsoleWarn.apply(console,e),_debugVisible&&debugWarn(...e)})}function _displayEarlyErrors(){window._earlyErrors&&window._earlyErrors.length>0&&(debugError(`🚨 Found ${window._earlyErrors.length} early error(s):`),window._earlyErrors.forEach(e=>{debugError(e.message),e.stack&&debugError("Stack trace:",e.stack)}),window._earlyErrors=[])}function _createDebugPanel(){if(_debugPanel)return;_debugPanel=document.createElement("div"),_debugPanel.id="mobile-debug-panel",_debugPanel.innerHTML='\n <div id="mobile-debug-header">\n <span>Debug</span>\n <button id="mobile-debug-close">×</button>\n </div>\n <div id="mobile-debug-content"></div>\n ';const e=document.createElement("style");e.textContent="\n #mobile-debug-panel {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 350px;\n max-width: calc(100vw - 40px);\n max-height: 400px;\n background: rgba(0, 0, 0, 0.9);\n color: #ffffff;\n font-family: 'Courier New', monospace;\n font-size: 12px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 10000;\n display: none;\n }\n \n #mobile-debug-header {\n background: rgba(255, 255, 255, 0.1);\n padding: 8px 12px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-radius: 8px 8px 0 0;\n }\n \n #mobile-debug-header span {\n font-weight: bold;\n font-size: 13px;\n }\n \n #mobile-debug-close {\n background: none;\n border: none;\n color: #ffffff;\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n #mobile-debug-close:hover {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n }\n \n #mobile-debug-content {\n padding: 12px;\n max-height: 340px;\n overflow-y: auto;\n word-wrap: break-word;\n line-height: 1.4;\n }\n \n .debug-message {\n margin-bottom: 4px;\n white-space: pre-wrap;\n }\n \n .debug-message.error {\n color: #ff6b6b;\n background: rgba(255, 107, 107, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ff6b6b;\n }\n \n .debug-message.warning {\n color: #ffd93d;\n background: rgba(255, 217, 61, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ffd93d;\n }\n \n .debug-timestamp {\n color: #888;\n font-size: 10px;\n }\n \n @media (max-width: 480px) {\n #mobile-debug-panel {\n width: calc(100vw - 20px);\n right: 10px;\n top: 10px;\n }\n }\n ",document.head.appendChild(e),document.body.appendChild(_debugPanel),document.getElementById("mobile-debug-close").onclick=hideDebug,_updateDebugDisplay()}function _updateDebugDisplay(){if(!_debugPanel)return;const e=document.getElementById("mobile-debug-content");e&&(e.innerHTML=_debugMessages.map(e=>"string"==typeof e?`<div class="debug-message">${e}</div>`:`<div class="debug-message ${e.type}">${e.text}</div>`).join(""),e.scrollTop=e.scrollHeight)}debug.clear=function(){_debugMessages=[],_debugPanel&&_updateDebugDisplay(),console.clear()},window.debug=debug,window.debugError=debugError,window.debugWarn=debugWarn,window.showDebug=showDebug,window.hideDebug=hideDebug,window.toggleDebug=toggleDebug,window.lockGestures=lockGestures,window.enableGyroTap=enableGyroTap,window.enableGyroButton=enableGyroButton,window.enableMicTap=enableMicTap,window.enableMicButton=enableMicButton,window.enableSoundTap=enableSoundTap,window.enableSoundButton=enableSoundButton,window.enableSpeechTap=enableSpeechTap,window.enableVibrationTap=enableVibrationTap,window.enableVibrationButton=enableVibrationButton,window.vibrate=vibrate,window.stopVibration=stopVibration,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton;class PhoneCamera{constructor(e="user",n=!0,t="fitHeight"){this._active=e,this._mirror=n,this._mode=t,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(){if(this._video&&this._video.elt&&this._video.elt.readyState>=2){if(this._onReadyCallback){const e=this._onReadyCallback;this._onReadyCallback=null,e()}}else setTimeout(()=>this._checkVideoReady(),100)}_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,t="undefined"!=typeof width?width:window.innerWidth,o="undefined"!=typeof height?height:window.innerHeight;let i,r,a,s;if("fixed"===this._mode)i=this._fixedWidth,r=this._fixedHeight,a=(t-i)/2,s=(o-r)/2;else if("fitWidth"===this._mode)i=t,r=n/e*i,a=0,s=(o-r)/2;else if("fitHeight"===this._mode)r=o,i=e/n*r,a=(t-i)/2,s=0;else if("cover"===this._mode){const d=Math.max(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}else if("contain"===this._mode){const d=Math.min(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}return{x:a,y:s,width:i,height:r,scaleX:i/e,scaleY:r/n}}mapPoint(e,n){const t=this.getDimensions();let o=e*t.scaleX;const i=n*t.scaleY;this._mirror&&(o=t.width-o);return{x:o+t.x,y:i+t.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)}_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,t="fitHeight"){return new PhoneCamera(e,n,t)}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.enableVibrationTap=enableVibrationTap,p5.prototype.enableVibrationButton=enableVibrationButton,p5.prototype.vibrate=vibrate,p5.prototype.stopVibration=stopVibration,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,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"));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<title>P5.js Mobile - Microphone Level</title>
|
|
5
5
|
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.10/lib/p5.min.js"></script>
|
|
6
6
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.0/addons/p5.sound.min.js"></script>
|
|
7
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
8
8
|
<style>
|
|
9
9
|
body {
|
|
10
10
|
margin: 0;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Speech Recognition Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates voice-to-text speech recognition using p5.js-speech library integrated with p5-phone for mobile microphone access.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
### HTTPS Required
|
|
8
|
+
Speech recognition requires a secure HTTPS connection on mobile devices. This will NOT work on:
|
|
9
|
+
- HTTP connections (except localhost)
|
|
10
|
+
- File:// protocol
|
|
11
|
+
|
|
12
|
+
### Browser Compatibility
|
|
13
|
+
|
|
14
|
+
**✅ Supported:**
|
|
15
|
+
- Chrome on Android (recommended)
|
|
16
|
+
- Chrome on Desktop
|
|
17
|
+
- Edge on Desktop
|
|
18
|
+
- Safari on Desktop (limited support)
|
|
19
|
+
|
|
20
|
+
**❌ NOT Supported:**
|
|
21
|
+
- **iOS Safari** - Apple's Safari on iOS does not support the Web Speech API
|
|
22
|
+
- **iOS Chrome** - Uses Safari's engine on iOS, so also not supported
|
|
23
|
+
- Firefox (as of 2025, limited support)
|
|
24
|
+
|
|
25
|
+
### Mobile Testing
|
|
26
|
+
To test on mobile:
|
|
27
|
+
1. Deploy to a server with HTTPS (GitHub Pages, Netlify, etc.)
|
|
28
|
+
2. Or use a tool like `ngrok` to create an HTTPS tunnel to your localhost
|
|
29
|
+
3. Make sure you're using Chrome on Android (not iOS)
|
|
30
|
+
|
|
31
|
+
## How It Works
|
|
32
|
+
|
|
33
|
+
### Cloud-Based Speech Recognition
|
|
34
|
+
|
|
35
|
+
The p5.speech library is built on top of the **Web Speech API**, which is a **cloud-based service**:
|
|
36
|
+
|
|
37
|
+
1. **Microphone captures audio** on your device (handled by p5-phone's `enableMicTap()`)
|
|
38
|
+
2. **Audio is sent to Google's servers** via the Web Speech Recognition API
|
|
39
|
+
3. **Google's servers process the audio** using machine learning models
|
|
40
|
+
4. **Transcribed text is sent back** to your browser
|
|
41
|
+
5. **Your sketch displays the results**
|
|
42
|
+
|
|
43
|
+
**IMPORTANT:** Because speech recognition happens on remote servers, you **MUST have an active internet connection** for this to work. If you get "no match" errors, check your internet connection first.
|
|
44
|
+
|
|
45
|
+
### Recognition Flow
|
|
46
|
+
|
|
47
|
+
1. **p5-phone** handles microphone permission via `enableMicTap()`
|
|
48
|
+
2. Once permission is granted, `userSetupComplete()` initializes p5.SpeechRec
|
|
49
|
+
3. Speech recognition starts and listens for phrases
|
|
50
|
+
4. Audio is sent to cloud for processing
|
|
51
|
+
5. Recognized text is displayed on screen with confidence levels
|
|
52
|
+
6. Recognition automatically restarts after each phrase
|
|
53
|
+
|
|
54
|
+
## Common Issues
|
|
55
|
+
|
|
56
|
+
### Desktop Works, Mobile Doesn't - Why?
|
|
57
|
+
|
|
58
|
+
**CRITICAL ISSUE IDENTIFIED:** Microphone hardware conflict!
|
|
59
|
+
|
|
60
|
+
**The Real Problem:**
|
|
61
|
+
- On mobile, only **ONE application can access the microphone at a time**
|
|
62
|
+
- `p5.AudioIn` (from p5.sound) and Web Speech API **both try to access the same mic**
|
|
63
|
+
- Desktop Chrome is more forgiving and allows both
|
|
64
|
+
- Mobile Chrome enforces strict exclusive access
|
|
65
|
+
|
|
66
|
+
**The Solution:**
|
|
67
|
+
- Create a `p5.AudioIn` object but **never call mic.start()**
|
|
68
|
+
- Use `enableMicTap()` to get permission, then **immediately stop the mic**
|
|
69
|
+
- This frees the hardware for Web Speech API to use
|
|
70
|
+
|
|
71
|
+
**Code pattern:**
|
|
72
|
+
```javascript
|
|
73
|
+
// In setup()
|
|
74
|
+
mic = new p5.AudioIn(); // Create but don't start
|
|
75
|
+
enableMicTap(); // Get permission
|
|
76
|
+
|
|
77
|
+
// In userSetupComplete()
|
|
78
|
+
if (mic && mic.enabled) {
|
|
79
|
+
mic.stop(); // Free the microphone!
|
|
80
|
+
}
|
|
81
|
+
speechRec = new p5.SpeechRec(); // Now this can access the mic
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Why this wasn't obvious:**
|
|
85
|
+
- Desktop allows multiple streams (mic.getLevel() AND speech recognition work together)
|
|
86
|
+
- Mobile enforces exclusive access (only ONE can work at a time)
|
|
87
|
+
- No error is thrown - speech API just silently fails to get audio
|
|
88
|
+
|
|
89
|
+
### Previous "No match found for speech" debugging
|
|
90
|
+
This is the most common error. It means the Web Speech API heard you speaking but couldn't transcribe it.
|
|
91
|
+
|
|
92
|
+
**Common causes:**
|
|
93
|
+
1. **Poor internet connection** - Audio couldn't be sent to Google's servers or response was delayed
|
|
94
|
+
2. **Background noise** - Other sounds interfering with your voice
|
|
95
|
+
3. **Speaking too quietly/quickly** - The API needs clear, distinct speech
|
|
96
|
+
4. **Language mismatch** - Currently set to `en-US`. Speak in English for best results.
|
|
97
|
+
|
|
98
|
+
**Solutions:**
|
|
99
|
+
- Check you have a stable internet connection
|
|
100
|
+
- Move to a quieter environment
|
|
101
|
+
- Speak more clearly and at a moderate pace
|
|
102
|
+
- Check the microphone level indicator - it should move when you speak
|
|
103
|
+
|
|
104
|
+
### "No speech detected" repeatedly
|
|
105
|
+
- Check that you're speaking clearly and loudly enough
|
|
106
|
+
- Ensure you're in a quiet environment
|
|
107
|
+
- Verify your device microphone is working (test with voice memos app)
|
|
108
|
+
- **Check internet connection** - without it, recognition cannot work
|
|
109
|
+
|
|
110
|
+
### Nothing happens on iOS
|
|
111
|
+
- iOS Safari does not support Web Speech API
|
|
112
|
+
- Try using an Android device with Chrome instead
|
|
113
|
+
|
|
114
|
+
### "Not allowed" error
|
|
115
|
+
- You may have denied microphone permission
|
|
116
|
+
- Reload the page and allow microphone access
|
|
117
|
+
- Check that you're using HTTPS (not HTTP)
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
You can adjust these settings in the code:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
speechRec.continuous = false; // false = one phrase at a time
|
|
125
|
+
speechRec.interimResults = true; // true = show partial results
|
|
126
|
+
const deviceLang = 'en-US'; // Change language (en-US, es-ES, etc.)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Privacy Note
|
|
130
|
+
|
|
131
|
+
This example uses `continuous: false` mode, which means:
|
|
132
|
+
- Recognition stops after each phrase
|
|
133
|
+
- More privacy-conscious than continuous listening
|
|
134
|
+
- Auto-restarts after 1 second pause
|
|
135
|
+
- User maintains control over when mic is active
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>P5.js Mobile - Speech Recognition</title>
|
|
5
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.10/lib/p5.min.js"></script>
|
|
6
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.0/addons/p5.sound.min.js"></script>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/gh/IDMNYU/p5.js-speech@0.0.3/lib/p5.speech.js"></script>
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<script src="sketch.js"></script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Speech Recognition example using p5.speech
|
|
2
|
+
// Touch to talk - tap screen, speak, release
|
|
3
|
+
// Based on p5.speech example: 04simplerecognition.html
|
|
4
|
+
|
|
5
|
+
let speechRec;
|
|
6
|
+
let recognizedText = "Tap screen and say something";
|
|
7
|
+
let isListening = false;
|
|
8
|
+
|
|
9
|
+
function setup() {
|
|
10
|
+
createCanvas(windowWidth, windowHeight);
|
|
11
|
+
background(50);
|
|
12
|
+
|
|
13
|
+
// Show debug panel for troubleshooting
|
|
14
|
+
showDebug();
|
|
15
|
+
|
|
16
|
+
// Lock gestures to prevent browser interference
|
|
17
|
+
lockGestures();
|
|
18
|
+
|
|
19
|
+
// Enable speech recognition permission
|
|
20
|
+
// This activates the audio context without creating p5.AudioIn
|
|
21
|
+
enableSpeechTap('Tap to enable speech recognition');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// This runs after speech permission is granted
|
|
25
|
+
function userSetupComplete() {
|
|
26
|
+
// Create speech recognition object
|
|
27
|
+
speechRec = new p5.SpeechRec('en-US');
|
|
28
|
+
speechRec.continuous = false; // One phrase per touch
|
|
29
|
+
speechRec.interimResults = false; // Only final results
|
|
30
|
+
speechRec.onResult = showResult;
|
|
31
|
+
|
|
32
|
+
// Handle when recognition ends
|
|
33
|
+
speechRec.onEnd = function() {
|
|
34
|
+
isListening = false;
|
|
35
|
+
console.log("Recognition ended");
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
console.log('✅ Speech recognition ready - tap screen to talk');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function draw() {
|
|
42
|
+
// Change background color when listening
|
|
43
|
+
if (isListening) {
|
|
44
|
+
background(100, 200, 100); // Green when listening
|
|
45
|
+
} else {
|
|
46
|
+
background(50);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fill(255);
|
|
50
|
+
textSize(32);
|
|
51
|
+
textAlign(CENTER, CENTER);
|
|
52
|
+
|
|
53
|
+
if (speechRec && window.speechEnabled) {
|
|
54
|
+
if (isListening) {
|
|
55
|
+
text("🎤 Listening...", width/2, 50);
|
|
56
|
+
} else {
|
|
57
|
+
text("👆 Tap to talk", width/2, 50);
|
|
58
|
+
}
|
|
59
|
+
text(recognizedText, 40, height/2, width - 80);
|
|
60
|
+
} else {
|
|
61
|
+
text("Tap to enable speech recognition", width/2, height/2);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Handle speech recognition results
|
|
66
|
+
function showResult() {
|
|
67
|
+
if (speechRec.resultValue) {
|
|
68
|
+
recognizedText = speechRec.resultString;
|
|
69
|
+
console.log("Recognized:", recognizedText);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Touch started - start listening
|
|
74
|
+
function touchStarted() {
|
|
75
|
+
if (speechRec && window.speechEnabled && !isListening) {
|
|
76
|
+
isListening = true;
|
|
77
|
+
speechRec.start();
|
|
78
|
+
console.log("Started listening...");
|
|
79
|
+
}
|
|
80
|
+
return false; // Prevent default
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Touch ended - stop listening
|
|
84
|
+
function touchEnded() {
|
|
85
|
+
if (speechRec && isListening) {
|
|
86
|
+
isListening = false;
|
|
87
|
+
// Don't call stop - just let it process what was said
|
|
88
|
+
console.log("Stopped listening...");
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/addons/p5.sound.min.js"></script>
|
|
22
22
|
|
|
23
23
|
<!-- Load the mobile p5.js permissions library -->
|
|
24
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
24
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
25
25
|
|
|
26
26
|
</head>
|
|
27
27
|
<body>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/addons/p5.sound.min.js"></script>
|
|
22
22
|
|
|
23
23
|
<!-- Load the mobile p5.js permissions library -->
|
|
24
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
24
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
25
25
|
|
|
26
26
|
</head>
|
|
27
27
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
19
|
|
|
20
20
|
<!-- Load the mobile p5.js permissions library -->
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
22
22
|
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<title>P5.js Mobile - Mic Level (Minimal)</title>
|
|
5
5
|
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.0/lib/p5.min.js"></script>
|
|
6
6
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.0/addons/p5.sound.min.js"></script>
|
|
7
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
8
8
|
<style>
|
|
9
9
|
body {
|
|
10
10
|
margin: 0;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<title>P5.js Mobile - Orientation Basic (Minimal)</title>
|
|
5
5
|
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.0/lib/p5.min.js"></script>
|
|
6
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
6
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
7
7
|
<style>
|
|
8
8
|
body {
|
|
9
9
|
margin: 0;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<title>P5.js Mobile - Rotational Velocity (Minimal)</title>
|
|
5
5
|
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.0/lib/p5.min.js"></script>
|
|
6
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.
|
|
6
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.3/dist/p5-phone.min.js"></script>
|
|
7
7
|
<style>
|
|
8
8
|
body {
|
|
9
9
|
margin: 0;
|