p5-phone 1.6.1 → 1.6.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 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.5.0/dist/p5-phone.min.js"></script>
70
+ <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/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.5.0/dist/p5-phone.js"></script> -->
73
+ <!-- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/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.5.0/dist/p5-phone.min.js"></script>
101
+ <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"></script>
102
102
 
103
103
  </head>
104
104
  <body>
package/dist/p5-phone.js CHANGED
@@ -1367,8 +1367,11 @@ class PhoneCamera {
1367
1367
 
1368
1368
  const videoWidth = this._video.width;
1369
1369
  const videoHeight = this._video.height;
1370
- const canvasWidth = window.width || window.innerWidth;
1371
- const canvasHeight = window.height || window.innerHeight;
1370
+
1371
+ // Get actual canvas dimensions (works on both desktop and mobile)
1372
+ // Use p5.js width/height globals if available, otherwise fall back to window dimensions
1373
+ const canvasWidth = (typeof width !== 'undefined') ? width : window.innerWidth;
1374
+ const canvasHeight = (typeof height !== 'undefined') ? height : window.innerHeight;
1372
1375
 
1373
1376
  let drawWidth, drawHeight, drawX, drawY;
1374
1377
 
@@ -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=window.width||window.innerWidth,o=window.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+Math.max(0,t.x),y:i+Math.max(0,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();push(),this._mirror?(translate(e.x+e.width,e.y),scale(-1,1),image(this._video,0,0,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){const t=n[0];if(n.length>=3){if(!t.ready||!t.video)return;const o=n[1],i=n[2],r=n[3]||t.width,a=n[4]||t.height;this.push(),t.mirror?(this.translate(o+r,i),this.scale(-1,1),e.call(this,t.video,0,0,r,a)):e.call(this,t.video,o,i,r,a),this.pop()}else t._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 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+Math.max(0,t.x),y:i+Math.max(0,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();push(),this._mirror?(translate(e.x+e.width,e.y),scale(-1,1),image(this._video,0,0,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){const t=n[0];if(n.length>=3){if(!t.ready||!t.video)return;const o=n[1],i=n[2],r=n[3]||t.width,a=n[4]||t.height;this.push(),t.mirror?(this.translate(o+r,i),this.scale(-1,1),e.call(this,t.video,0,0,r,a)):e.call(this,t.video,o,i,r,a),this.pop()}else t._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"));
@@ -535,14 +535,14 @@
535
535
 
536
536
  <div class="code-box">
537
537
  <h4>Minified version (recommended)</h4>
538
- <pre><code>&lt;script src="https://cdn.jsdelivr.net/npm/p5-phone@1.5.0/dist/p5-phone.min.js"&gt;&lt;/script&gt;</code></pre>
539
- <button class="copy-button" onclick="copyCode(this, '&lt;script src=&quot;https://cdn.jsdelivr.net/npm/p5-phone@1.5.0/dist/p5-phone.min.js&quot;&gt;&lt;/script&gt;')">Copy</button>
538
+ <pre><code>&lt;script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"&gt;&lt;/script&gt;</code></pre>
539
+ <button class="copy-button" onclick="copyCode(this, '&lt;script src=&quot;https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js&quot;&gt;&lt;/script&gt;')">Copy</button>
540
540
  </div>
541
541
 
542
542
  <div class="code-box">
543
543
  <h4>Development version (larger, with comments)</h4>
544
- <pre><code>&lt;script src="https://cdn.jsdelivr.net/npm/p5-phone@1.5.0/dist/p5-phone.js"&gt;&lt;/script&gt;</code></pre>
545
- <button class="copy-button" onclick="copyCode(this, '&lt;script src=&quot;https://cdn.jsdelivr.net/npm/p5-phone@1.5.0/dist/p5-phone.js&quot;&gt;&lt;/script&gt;')">Copy</button>
544
+ <pre><code>&lt;script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.js"&gt;&lt;/script&gt;</code></pre>
545
+ <button class="copy-button" onclick="copyCode(this, '&lt;script src=&quot;https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.js&quot;&gt;&lt;/script&gt;')">Copy</button>
546
546
  </div>
547
547
  </div>
548
548
 
@@ -608,7 +608,7 @@
608
608
  &lt;/style&gt;
609
609
 
610
610
  &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"&gt;&lt;/script&gt;
611
- &lt;script src="https://cdn.jsdelivr.net/npm/p5-phone@1.5.0/dist/p5-phone.min.js"&gt;&lt;/script&gt;
611
+ &lt;script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"&gt;&lt;/script&gt;
612
612
  &lt;/head&gt;
613
613
  &lt;body&gt;
614
614
  &lt;script src="sketch.js"&gt;&lt;/script&gt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "p5-phone",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease",
5
5
  "main": "src/p5-phone.js",
6
6
  "files": [
package/src/p5-phone.js CHANGED
@@ -1367,8 +1367,11 @@ class PhoneCamera {
1367
1367
 
1368
1368
  const videoWidth = this._video.width;
1369
1369
  const videoHeight = this._video.height;
1370
- const canvasWidth = window.width || window.innerWidth;
1371
- const canvasHeight = window.height || window.innerHeight;
1370
+
1371
+ // Get actual canvas dimensions (works on both desktop and mobile)
1372
+ // Use p5.js width/height globals if available, otherwise fall back to window dimensions
1373
+ const canvasWidth = (typeof width !== 'undefined') ? width : window.innerWidth;
1374
+ const canvasHeight = (typeof height !== 'undefined') ? height : window.innerHeight;
1372
1375
 
1373
1376
  let drawWidth, drawHeight, drawX, drawY;
1374
1377
 
@@ -1,119 +0,0 @@
1
- # Camera Selector - PhoneCamera API Demo
2
-
3
- This example demonstrates the full capabilities of the **PhoneCamera** class from p5-phone library.
4
-
5
- ## Features Demonstrated
6
-
7
- ### 1. Camera Switching
8
- - Toggle between front (`'user'`) and back (`'environment'`) cameras
9
- - Instant switching with `cam.active = 'environment'`
10
- - No manual camera recreation needed
11
-
12
- ### 2. Display Modes
13
- Cycle through all 5 display modes:
14
- - **fitHeight** - Fill canvas height, maintain aspect ratio (default)
15
- - **fitWidth** - Fill canvas width, maintain aspect ratio
16
- - **cover** - Fill entire canvas, crop if needed
17
- - **contain** - Fit entirely within canvas, letterbox if needed
18
- - **fixed** - Use fixed dimensions (640x480 default)
19
-
20
- ### 3. Mirroring Control
21
- - Toggle horizontal flip with `cam.mirror = true/false`
22
- - Typically ON for front camera (natural selfie view)
23
- - Typically OFF for back camera
24
-
25
- ### 4. Status Display
26
- - Real-time display of current settings
27
- - Shows video dimensions and display dimensions
28
- - Demonstrates `cam.getDimensions()` for debugging
29
-
30
- ## Code Comparison
31
-
32
- ### Original Version (331 lines):
33
- ```javascript
34
- // Manual camera management
35
- function startCamera(facingMode) {
36
- if (videoCapture) videoCapture.remove();
37
- let constraints = { video: { facingMode }, audio: false, flipped: isFlipped };
38
- videoCapture = createCapture(constraints);
39
- videoCapture.hide();
40
- }
41
-
42
- // Manual dimension calculations (35+ lines)
43
- function drawVideoFeed() {
44
- let videoWidth = videoCapture.width;
45
- let videoHeight = videoCapture.height;
46
- if (scaleMode === 'fitWidth') {
47
- drawWidth = width;
48
- drawHeight = (videoHeight / videoWidth) * width;
49
- // ... more calculations
50
- }
51
- // ... more modes
52
- image(videoCapture, drawX, drawY, drawWidth, drawHeight);
53
- }
54
- ```
55
-
56
- ### New Version (~220 lines):
57
- ```javascript
58
- // Create once in setup
59
- cam = createPhoneCamera('user', true, 'fitHeight');
60
-
61
- // Switch cameras with property
62
- cam.active = 'environment';
63
-
64
- // Change modes with property
65
- cam.mode = 'cover';
66
-
67
- // Draw with one line
68
- image(cam, 0, 0); // All positioning automatic!
69
- ```
70
-
71
- ## Key Benefits
72
-
73
- ✅ **33% Less Code** - From 331 to ~220 lines
74
- ✅ **No Manual Calculations** - All dimension math handled by library
75
- ✅ **Property-Based API** - Simple assignments instead of method calls
76
- ✅ **Automatic Mirroring** - Built into drawing, no manual transforms
77
- ✅ **5 Display Modes** - More options than original (added cover, contain)
78
- ✅ **Status Info** - Easy access to dimensions via `getDimensions()`
79
-
80
- ## Usage Tips
81
-
82
- ### Camera Switching
83
- ```javascript
84
- // Instant switch
85
- cam.active = cam.active === 'user' ? 'environment' : 'user';
86
-
87
- // Typically adjust mirroring when switching
88
- if (cam.active === 'environment') {
89
- cam.mirror = false; // Back camera usually not mirrored
90
- } else {
91
- cam.mirror = true; // Front camera mirrored for selfie
92
- }
93
- ```
94
-
95
- ### Display Mode Selection
96
- - **Mobile Portrait**: Use `fitHeight` (default) - fills screen vertically
97
- - **Mobile Landscape**: Use `fitWidth` - fills screen horizontally
98
- - **Fill Screen**: Use `cover` - no letterboxing, may crop video
99
- - **See Full Video**: Use `contain` - shows entire video, may letterbox
100
- - **Fixed Size**: Use `fixed` with `cam.fixedWidth` and `cam.fixedHeight`
101
-
102
- ### Getting Dimension Info
103
- ```javascript
104
- let dims = cam.getDimensions();
105
- console.log(dims.x, dims.y); // Position on canvas
106
- console.log(dims.width, dims.height); // Display size
107
- console.log(dims.scaleX, dims.scaleY); // Scale factors for ML5
108
- ```
109
-
110
- ## Testing Instructions
111
-
112
- 1. Open on mobile device
113
- 2. Tap to enable camera
114
- 3. Use bottom buttons to:
115
- - **Left**: Switch between front/back cameras
116
- - **Middle**: Cycle through display modes
117
- - **Right**: Toggle mirroring on/off
118
- 4. Observe status overlay showing current settings
119
- 5. Rotate device to test different orientations
@@ -1,28 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>01 - Camera Selector</title>
7
-
8
- <!-- Basic CSS to remove browser defaults and align canvas -->
9
- <style>
10
- body {
11
- margin: 0;
12
- padding: 0;
13
- overflow: hidden;
14
- }
15
- </style>
16
-
17
- <!-- Load p5.js library -->
18
- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
19
-
20
- <!-- Load p5-phone library with camera support -->
21
- <script src="../../../src/p5-phone.js"></script>
22
-
23
- </head>
24
- <body>
25
- <!-- Load the p5.js sketch -->
26
- <script src="sketch.js"></script>
27
- </body>
28
- </html>
@@ -1,239 +0,0 @@
1
- // ==============================================
2
- // CAMERA SELECTOR - SIMPLIFIED WITH P5-PHONE
3
- // ==============================================
4
- // Demonstrates the PhoneCamera API from p5-phone library.
5
- // Shows camera switching, display modes, and mirroring.
6
- //
7
- // KEY FEATURES:
8
- // - Switch between front/back cameras
9
- // - Cycle through display modes (fitHeight, fitWidth, cover, contain, fixed)
10
- // - Toggle mirroring on/off
11
- // - All camera logic handled by PhoneCamera class
12
- // ==============================================
13
-
14
- // ==============================================
15
- // GLOBAL VARIABLES
16
- // ==============================================
17
- let cam; // PhoneCamera instance
18
- let cameraButton; // Button to switch cameras
19
- let scaleModeButton; // Button to change scale mode
20
- let flipButton; // Button to toggle video flip
21
-
22
- // ==============================================
23
- // SETUP FUNCTION - Runs once when page loads
24
- // ==============================================
25
- function setup() {
26
- createCanvas(windowWidth, windowHeight);
27
-
28
- // Lock mobile gestures to prevent browser interference
29
- lockGestures();
30
-
31
- // Create PhoneCamera: front camera, mirrored, fit to height
32
- cam = createPhoneCamera('user', true, 'fitHeight');
33
-
34
- // Create UI buttons
35
- createCameraButton();
36
- createScaleModeButton();
37
- createFlipButton();
38
-
39
- // Enable camera with tap
40
- enableCameraTap();
41
- }
42
-
43
- // ==============================================
44
- // CAMERA READY - Called when user enables camera
45
- // ==============================================
46
- function userCameraReady() {
47
- // Initialize the camera (starts video capture)
48
- cam._initializeCamera();
49
- console.log('Camera started!');
50
- }
51
-
52
- // ==============================================
53
- // DRAW FUNCTION - Runs continuously
54
- // ==============================================
55
- function draw() {
56
- background(40);
57
-
58
- // Draw the camera feed if ready
59
- if (cam.ready) {
60
- image(cam, 0, 0); // PhoneCamera handles all positioning and mirroring!
61
- } else {
62
- // Show loading message
63
- fill(255);
64
- textAlign(CENTER, CENTER);
65
- textSize(24);
66
- text('Tap to start camera...', width/2, height/2);
67
- }
68
-
69
- // Draw status overlay
70
- drawStatus();
71
- }
72
-
73
- // ==============================================
74
- // CREATE CAMERA BUTTON
75
- // ==============================================
76
- function createCameraButton() {
77
- cameraButton = createButton('Front Cam');
78
-
79
- let buttonWidth = width / 3;
80
- let buttonHeight = 60;
81
- let buttonX = 0;
82
- let buttonY = height - buttonHeight;
83
-
84
- cameraButton.position(buttonX, buttonY);
85
- cameraButton.size(buttonWidth, buttonHeight);
86
- cameraButton.style('font-size', '14px');
87
- cameraButton.style('background-color', '#4A90E2');
88
- cameraButton.style('color', 'white');
89
- cameraButton.style('border', 'none');
90
- cameraButton.style('border-radius', '0');
91
- cameraButton.style('cursor', 'pointer');
92
- cameraButton.style('box-shadow', 'none');
93
- cameraButton.style('z-index', '1000');
94
-
95
- cameraButton.mousePressed(switchCamera);
96
- }
97
-
98
- // ==============================================
99
- // CREATE SCALE MODE BUTTON
100
- // ==============================================
101
- function createScaleModeButton() {
102
- scaleModeButton = createButton('Fit Height');
103
-
104
- let buttonWidth = width / 3;
105
- let buttonHeight = 60;
106
- let buttonX = width / 3;
107
- let buttonY = height - buttonHeight;
108
-
109
- scaleModeButton.position(buttonX, buttonY);
110
- scaleModeButton.size(buttonWidth, buttonHeight);
111
- scaleModeButton.style('font-size', '14px');
112
- scaleModeButton.style('background-color', '#50C878');
113
- scaleModeButton.style('color', 'white');
114
- scaleModeButton.style('border', 'none');
115
- scaleModeButton.style('border-radius', '0');
116
- scaleModeButton.style('cursor', 'pointer');
117
- scaleModeButton.style('box-shadow', 'none');
118
- scaleModeButton.style('z-index', '1000');
119
-
120
- scaleModeButton.mousePressed(cycleScaleMode);
121
- }
122
-
123
- // ==============================================
124
- // CREATE FLIP BUTTON
125
- // ==============================================
126
- function createFlipButton() {
127
- flipButton = createButton('Mirror: ON');
128
-
129
- let buttonWidth = width / 3;
130
- let buttonHeight = 60;
131
- let buttonX = (width / 3) * 2;
132
- let buttonY = height - buttonHeight;
133
-
134
- flipButton.position(buttonX, buttonY);
135
- flipButton.size(buttonWidth, buttonHeight);
136
- flipButton.style('font-size', '14px');
137
- flipButton.style('background-color', '#FF6B6B');
138
- flipButton.style('color', 'white');
139
- flipButton.style('border', 'none');
140
- flipButton.style('border-radius', '0');
141
- flipButton.style('cursor', 'pointer');
142
- flipButton.style('box-shadow', 'none');
143
- flipButton.style('z-index', '1000');
144
-
145
- flipButton.mousePressed(toggleFlip);
146
- }
147
-
148
- // ==============================================
149
- // SWITCH CAMERA
150
- // ==============================================
151
- function switchCamera() {
152
- if (!cam.ready) return;
153
-
154
- // Toggle camera with property assignment
155
- cam.active = cam.active === 'user' ? 'environment' : 'user';
156
-
157
- // Update button text
158
- cameraButton.html(cam.active === 'user' ? 'Front Cam' : 'Back Cam');
159
- }
160
-
161
- // ==============================================
162
- // CYCLE SCALE MODE
163
- // ==============================================
164
- function cycleScaleMode() {
165
- if (!cam.ready) return;
166
-
167
- // Cycle through: fitHeight -> fitWidth -> cover -> contain -> fixed -> fitHeight
168
- const modes = ['fitHeight', 'fitWidth', 'cover', 'contain', 'fixed'];
169
- const modeNames = ['Fit Height', 'Fit Width', 'Cover', 'Contain', 'Fixed'];
170
-
171
- let currentIndex = modes.indexOf(cam.mode);
172
- let nextIndex = (currentIndex + 1) % modes.length;
173
-
174
- cam.mode = modes[nextIndex];
175
- scaleModeButton.html(modeNames[nextIndex]);
176
- }
177
-
178
- // ==============================================
179
- // TOGGLE FLIP
180
- // ==============================================
181
- function toggleFlip() {
182
- if (!cam.ready) return;
183
-
184
- // Toggle mirroring with property assignment
185
- cam.mirror = !cam.mirror;
186
-
187
- // Update button text
188
- flipButton.html(cam.mirror ? 'Mirror: ON' : 'Mirror: OFF');
189
- }
190
-
191
- // ==============================================
192
- // DRAW STATUS
193
- // ==============================================
194
- function drawStatus() {
195
- if (!cam.ready) return;
196
-
197
- // Draw status overlay at top
198
- push();
199
- fill(0, 0, 0, 150);
200
- noStroke();
201
- rect(0, 0, width, 80);
202
-
203
- fill(255);
204
- textAlign(CENTER, CENTER);
205
- textSize(16);
206
- text(`Camera: ${cam.active}`, width/2, 20);
207
- text(`Mode: ${cam.mode} | Mirror: ${cam.mirror ? 'ON' : 'OFF'}`, width/2, 45);
208
-
209
- // Show dimensions
210
- let dims = cam.getDimensions();
211
- textSize(12);
212
- text(`Display: ${dims.width.toFixed(0)}x${dims.height.toFixed(0)} | Video: ${cam.video.width}x${cam.video.height}`, width/2, 65);
213
- pop();
214
- }
215
-
216
- // ==============================================
217
- // WINDOW RESIZE
218
- // ==============================================
219
- function windowResized() {
220
- resizeCanvas(windowWidth, windowHeight);
221
-
222
- // Recalculate button dimensions and positions
223
- let buttonWidth = width / 3;
224
- let buttonHeight = 60;
225
- let buttonY = height - buttonHeight;
226
-
227
- if (cameraButton) {
228
- cameraButton.position(0, buttonY);
229
- cameraButton.size(buttonWidth, buttonHeight);
230
- }
231
- if (scaleModeButton) {
232
- scaleModeButton.position(buttonWidth, buttonY);
233
- scaleModeButton.size(buttonWidth, buttonHeight);
234
- }
235
- if (flipButton) {
236
- flipButton.position(buttonWidth * 2, buttonY);
237
- flipButton.size(buttonWidth, buttonHeight);
238
- }
239
- }
@@ -1,34 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>03 - FaceMesh Nose</title>
7
-
8
- <!-- Basic CSS to remove browser defaults and align canvas -->
9
- <style>
10
- body {
11
- margin: 0;
12
- padding: 0;
13
- overflow: hidden;
14
- }
15
- </style>
16
-
17
- <!-- Load p5.js library -->
18
- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
19
-
20
- <!-- Load p5.sound library for microphone -->
21
- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.0/addons/p5.sound.min.js"></script>
22
-
23
- <!-- Load ml5.js library -->
24
- <script src="https://unpkg.com/ml5@1/dist/ml5.min.js"></script>
25
-
26
- <!-- Load p5-phone library for mobile sensors -->
27
- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.5.0/dist/p5-phone.min.js"></script>
28
-
29
- </head>
30
- <body>
31
- <!-- Load the p5.js sketch -->
32
- <script src="sketch.js"></script>
33
- </body>
34
- </html>