p5-phone 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +133 -9
  2. package/dist/p5-phone.js +291 -18
  3. package/dist/p5-phone.min.js +1 -1
  4. package/examples/Phone Sensor Examples/microphone/01_mic_level/index.html +1 -1
  5. package/examples/Phone Sensor Examples/microphone/01_mic_level/sketch.js +3 -3
  6. package/examples/Phone Sensor Examples/microphone/02_speech_recognition/README.md +1 -1
  7. package/examples/Phone Sensor Examples/microphone/02_speech_recognition/index.html +1 -1
  8. package/examples/Phone Sensor Examples/microphone/02_speech_recognition/sketch.js +2 -2
  9. package/examples/Phone Sensor Examples/movement/01_orientation_basic/index.html +2 -1
  10. package/examples/Phone Sensor Examples/movement/01_orientation_basic/sketch.js +3 -3
  11. package/examples/Phone Sensor Examples/movement/02_rotational_velocity/index.html +2 -1
  12. package/examples/Phone Sensor Examples/movement/02_rotational_velocity/sketch.js +3 -3
  13. package/examples/Phone Sensor Examples/movement/03_acceleration/index.html +2 -1
  14. package/examples/Phone Sensor Examples/movement/03_acceleration/sketch.js +3 -3
  15. package/examples/Phone Sensor Examples/nfc/01_nfc_read/index.html +29 -0
  16. package/examples/Phone Sensor Examples/nfc/01_nfc_read/sketch.js +60 -0
  17. package/examples/Phone Sensor Examples/sound/01_dual_audio/index.html +2 -1
  18. package/examples/Phone Sensor Examples/sound/01_dual_audio/sketch.js +5 -5
  19. package/examples/Phone Sensor Examples/sound/02_volume_touches/index.html +2 -1
  20. package/examples/Phone Sensor Examples/sound/02_volume_touches/sketch.js +7 -7
  21. package/examples/Phone Sensor Examples/touch/01_touch_basic/index.html +2 -1
  22. package/examples/Phone Sensor Examples/touch/01_touch_basic/sketch.js +4 -4
  23. package/examples/Phone Sensor Examples/touch/02_touch_zones/index.html +2 -1
  24. package/examples/Phone Sensor Examples/touch/02_touch_zones/sketch.js +3 -3
  25. package/examples/Phone Sensor Examples/touch/03_touch_count/index.html +2 -1
  26. package/examples/Phone Sensor Examples/touch/03_touch_count/sketch.js +3 -3
  27. package/examples/Phone Sensor Examples/touch/04_touch_distance/index.html +2 -1
  28. package/examples/Phone Sensor Examples/touch/04_touch_distance/sketch.js +3 -3
  29. package/examples/Phone Sensor Examples/touch/05_touch_angle/index.html +2 -1
  30. package/examples/Phone Sensor Examples/touch/05_touch_angle/sketch.js +3 -3
  31. package/examples/Phone Sensor Examples/vibration/01_haptic_feedback/index.html +2 -1
  32. package/examples/Phone Sensor Examples/vibration/01_haptic_feedback/sketch.js +3 -3
  33. package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/index.html +1 -1
  34. package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/index.html +1 -1
  35. package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/index.html +1 -1
  36. package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/index.html +1 -1
  37. package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/index.html +1 -1
  38. package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/sketch.js +2 -2
  39. package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/index.html +1 -1
  40. package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/sketch.js +2 -2
  41. package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/index.html +1 -1
  42. package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/sketch.js +2 -2
  43. package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/index.html +1 -1
  44. package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/sketch.js +2 -2
  45. package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/index.html +1 -1
  46. package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/sketch.js +2 -2
  47. package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/index.html +1 -1
  48. package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/sketch.js +2 -2
  49. package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/index.html +1 -1
  50. package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/sketch.js +2 -2
  51. package/examples/Phone Sensor Examples - Minimal/vibration/01_haptic_feedback/index.html +1 -1
  52. package/examples/Phone Sensor Examples - Minimal/vibration/01_haptic_feedback/sketch.js +2 -2
  53. package/examples/Phone and Gif/collision/index.html +1 -1
  54. package/examples/Phone and Gif/collision/sketch.js +3 -3
  55. package/examples/Phone and Gif/fetch/index.html +1 -1
  56. package/examples/Phone and Gif/fetch/sketch.js +3 -3
  57. package/examples/Phone and Gif/fly/index.html +1 -1
  58. package/examples/Phone and Gif/roll/index.html +1 -1
  59. package/examples/Phone and Gif/roll/sketch.js +3 -3
  60. package/examples/UIStyles/banner-style/index.html +1 -0
  61. package/examples/UIStyles/canvas-style/index.html +1 -0
  62. package/examples/UIStyles/custom-element/index.html +1 -0
  63. package/examples/UXcompare/button-vs-movement/index.html +2 -1
  64. package/examples/UXcompare/button-vs-orientation/index.html +2 -1
  65. package/examples/UXcompare/button-vs-shake/index.html +2 -1
  66. package/examples/UXcompare/gyroscope-demo/index.html +2 -1
  67. package/examples/UXcompare/microphone-demo/index.html +2 -1
  68. package/examples/UXcompare/slider-vs-angle/index.html +2 -1
  69. package/examples/UXcompare/slider-vs-angle/sketch.js +3 -3
  70. package/examples/UXcompare/slider-vs-distance/index.html +2 -1
  71. package/examples/UXcompare/slider-vs-distance/sketch.js +3 -3
  72. package/examples/UXcompare/slider-vs-microphone/index.html +2 -1
  73. package/examples/UXcompare/slider-vs-microphone/sketch.js +3 -3
  74. package/examples/UXcompare/slider-vs-touches/index.html +2 -1
  75. package/examples/UXcompare/slider-vs-touches/sketch.js +3 -3
  76. package/examples/UXcompare/sliders-vs-acceleration/index.html +2 -1
  77. package/examples/UXcompare/sliders-vs-acceleration/sketch.js +3 -3
  78. package/examples/UXcompare/sliders-vs-rotation/index.html +2 -1
  79. package/examples/UXcompare/sliders-vs-rotation/sketch.js +3 -3
  80. package/examples/blankTemplate/index.html +2 -1
  81. package/examples/blankTemplate/sketch.js +2 -2
  82. package/examples/homepage/index.html +10 -9
  83. package/examples/ml5/Gaze_detector_class/index.html +2 -1
  84. package/examples/ml5/PHONE_BodyPose_two_points/index.html +2 -1
  85. package/examples/ml5/PHONE_FaceMesh_two_points/index.html +2 -1
  86. package/examples/ml5/PHONE_HandPose_two_points/index.html +2 -1
  87. package/package.json +1 -1
  88. package/src/p5-phone.js +291 -18
package/README.md CHANGED
@@ -17,9 +17,11 @@ That's where this library comes in:
17
17
 
18
18
  This library simplifies access to the following p5.js mobile sensor and audio commands:
19
19
 
20
- **Touch Events:**
21
- - [`touchStarted()`](https://p5js.org/reference/p5/touchStarted/) - Called when a touch begins
22
- - [`touchEnded()`](https://p5js.org/reference/p5/touchEnded/) - Called when a touch ends
20
+ **Touch/Pointer Events:**
21
+ - [`mousePressed()`](https://p5js.org/reference/p5/mousePressed/) - Called when a press/touch begins (works for both mouse and touch in p5.js 1.x and 2.0)
22
+ - [`mouseReleased()`](https://p5js.org/reference/p5/mouseReleased/) - Called when a press/touch ends (works for both mouse and touch in p5.js 1.x and 2.0)
23
+ - [`touchStarted()`](https://p5js.org/reference/p5/touchStarted/) - Called when a touch begins (p5.js 1.x only)
24
+ - [`touchEnded()`](https://p5js.org/reference/p5/touchEnded/) - Called when a touch ends (p5.js 1.x only)
23
25
 
24
26
  **Device Motion & Orientation:**
25
27
  - [`rotationX`](https://p5js.org/reference/p5/rotationX/) - Device tilt forward/backward
@@ -45,10 +47,32 @@ This library simplifies access to the following p5.js mobile sensor and audio co
45
47
  - Safari 13+
46
48
  - Firefox 75+
47
49
 
50
+ ## p5.js Version Compatibility
51
+
52
+ p5-phone supports both **p5.js 1.x** and **p5.js 2.0+**.
53
+
54
+ | Feature | p5.js 1.x | p5.js 2.0+ |
55
+ |---------|-----------|------------|
56
+ | Permission UI (Tap/Button/Canvas/Banner/Custom) | ✅ | ✅ |
57
+ | Motion sensors (rotationX/Y/Z, accelerationX/Y/Z) | ✅ | ✅ |
58
+ | Microphone / Speech / Sound | ✅ | ✅ |
59
+ | Camera (PhoneCamera) | ✅ | ✅ |
60
+ | Vibration | ✅ | ✅ |
61
+ | NFC Tag Reading (Android only) | ✅ | ✅ |
62
+ | Debug console | ✅ | ✅ |
63
+ | lockGestures() | ✅ | ✅ |
64
+ | `touchStarted()` / `touchEnded()` | ✅ | ❌ Use `mousePressed()` / `mouseReleased()` |
65
+ | `p5.registerAddon()` | ❌ | ✅ (auto-detected) |
66
+
67
+ **Key change in p5.js 2.0:** Touch-specific callbacks (`touchStarted`, `touchMoved`, `touchEnded`) are no longer dispatched. The unified Pointer API routes all input (mouse + touch) through `mousePressed()`, `mouseDragged()`, and `mouseReleased()`. These mouse callbacks work in **both** p5.js 1.x and 2.0, so use them for forward-compatible code.
68
+
69
+ p5-phone automatically detects the p5.js version and adjusts its internal touch override behavior accordingly. No configuration needed.
70
+
48
71
  ## Table of Contents
49
72
 
50
73
  - [Link for Interactive Examples](#link-for-interactive-examples)
51
74
  - [Browser Compatibility](#browser-compatibility)
75
+ - [p5.js Version Compatibility](#p5js-version-compatibility)
52
76
  - [CDN (Recommended)](#cdn-recommended)
53
77
  - [Basic Setup](#basic-setup)
54
78
  - [Index HTML](#index-html)
@@ -63,6 +87,7 @@ This library simplifies access to the following p5.js mobile sensor and audio co
63
87
  - [Speech Recognition Activation](#speech-recognition-activation)
64
88
  - [Combined Activation](#combined-activation)
65
89
  - [Vibration Motor (Android Only)](#vibration-motor-android-only)
90
+ - [NFC Tag Reading (Android Only)](#nfc-tag-reading-android-only)
66
91
  - [PhoneCamera (ML5 Integration)](#phonecamera-ml5-integration)
67
92
  - [Debug System](#debug-system)
68
93
  - [Permission UI Styles](#permission-ui-styles)
@@ -77,10 +102,10 @@ This library simplifies access to the following p5.js mobile sensor and audio co
77
102
 
78
103
  ```html
79
104
  <!-- Minified version (recommended) -->
80
- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.7.0/dist/p5-phone.min.js"></script>
105
+ <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.0/dist/p5-phone.min.js"></script>
81
106
 
82
107
  <!-- Development version (larger, with comments) -->
83
- <!-- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.7.0/dist/p5-phone.js"></script> -->
108
+ <!-- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.0/dist/p5-phone.js"></script> -->
84
109
  ```
85
110
 
86
111
  ### Basic Setup
@@ -106,9 +131,10 @@ This library simplifies access to the following p5.js mobile sensor and audio co
106
131
 
107
132
  <!-- Load p5.js library -->
108
133
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
134
+ <!-- For p5.js 2.0: <script src="https://cdn.jsdelivr.net/npm/p5@2/lib/p5.min.js"></script> -->
109
135
 
110
136
  <!-- Load p5-phone library -->
111
- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.7.0/dist/p5-phone.min.js"></script>
137
+ <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.9.0/dist/p5-phone.min.js"></script>
112
138
 
113
139
  </head>
114
140
  <body>
@@ -173,11 +199,12 @@ function draw() {
173
199
  }
174
200
 
175
201
  // Prevent default touch behavior (optional but recommended)
176
- function touchStarted() {
202
+ // Use mousePressed/mouseReleased — works in both p5.js 1.x and 2.0
203
+ function mousePressed() {
177
204
  return false;
178
205
  }
179
206
 
180
- function touchEnded() {
207
+ function mouseReleased() {
181
208
  return false;
182
209
  }
183
210
  ```
@@ -277,6 +304,7 @@ this.enableGyroTap('Tap to start');
277
304
  - `window.soundEnabled` - Boolean indicating if sound output is active
278
305
  - `window.speechEnabled` - Boolean indicating if speech recognition is active
279
306
  - `window.vibrationEnabled` - Boolean indicating if vibration is available (Android only)
307
+ - `window.nfcEnabled` - Boolean indicating if NFC scanning is active (Android only)
280
308
 
281
309
  **Usage:**
282
310
  ```javascript
@@ -301,6 +329,11 @@ function draw() {
301
329
  // Safe to use vibration (Android only)
302
330
  vibrate(50);
303
331
  }
332
+
333
+ if (window.nfcEnabled) {
334
+ // NFC scanning is active (Android only)
335
+ // Tag data arrives via nfcRead() callback
336
+ }
304
337
  }
305
338
 
306
339
  // You can also use them for conditional UI
@@ -580,7 +613,7 @@ function mousePressed() {
580
613
  }
581
614
 
582
615
  // Touch zones with different haptic patterns
583
- function touchStarted() {
616
+ function mousePressed() {
584
617
  if (window.vibrationEnabled) {
585
618
  if (mouseX < width/2) {
586
619
  vibrate(50); // Left side - short pulse
@@ -613,6 +646,94 @@ function gameOver() {
613
646
  - Don't overuse - vibration can quickly drain battery
614
647
  - Test on Android devices as iOS doesn't support vibration
615
648
 
649
+ ### NFC Tag Reading (Android Only)
650
+
651
+ **Purpose:** Read NFC (Near Field Communication) tags using the Web NFC API. Ideal for interactive installations, scavenger hunts, or any sketch that responds to physical NFC tags.
652
+
653
+ **⚠️ Platform Support:**
654
+ - ✅ **Android** - Chrome 89+ and Samsung Internet 15+
655
+ - ❌ **iOS** - Not supported (Web NFC API not available on iOS)
656
+ - Requires **HTTPS** — NFC is blocked on insecure origins
657
+
658
+ **Important:** The Web NFC API requires user activation (a tap or click) before scanning can begin — the same pattern used by all other p5-phone permission functions. On unsupported devices/browsers, `window.nfcEnabled` will be `false` and calls will be safely ignored with console warnings.
659
+
660
+ **Commands:**
661
+ - `enableNfcTap(message)` - Tap anywhere on screen to enable NFC scanning
662
+ - `enableNfcButton(text)` - Creates a button with custom text to enable NFC
663
+ - `stopNfc()` - Stop NFC scanning
664
+
665
+ **Status Variables:**
666
+ - `window.nfcEnabled` - Boolean indicating if NFC scanning is active
667
+ - `window.lastNfcMessage` - Object containing the most recently read tag's data
668
+ - `window.lastNfcSerialNumber` - Serial number string of the most recently read tag
669
+
670
+ **User Callback:**
671
+
672
+ Define an `nfcRead()` function in your sketch to receive tag data when a tag is scanned:
673
+
674
+ ```javascript
675
+ function nfcRead(message, serialNumber) {
676
+ // message.serialNumber — tag serial number
677
+ // message.records — array of NDEF records, each with:
678
+ // .recordType — 'text', 'url', 'mime', etc.
679
+ // .data — decoded content (string for text/url, object for JSON, raw for others)
680
+ // .mediaType — MIME type (for 'mime' records)
681
+ // .id — record id (if present)
682
+ // .raw — original DataView
683
+ }
684
+ ```
685
+
686
+ **Usage:**
687
+ ```javascript
688
+ let tagText = 'No tag scanned yet';
689
+
690
+ function setup() {
691
+ createCanvas(windowWidth, windowHeight);
692
+ lockGestures();
693
+ enableNfcTap('Tap to enable NFC');
694
+ }
695
+
696
+ function draw() {
697
+ background(220);
698
+ textAlign(CENTER, CENTER);
699
+ textSize(20);
700
+
701
+ if (!window.nfcEnabled) {
702
+ text('NFC not active', width / 2, height / 2);
703
+ } else {
704
+ text(tagText, width / 2, height / 2);
705
+ text('Hold an NFC tag near your phone', width / 2, height / 2 + 40);
706
+ }
707
+ }
708
+
709
+ function nfcRead(message, serialNumber) {
710
+ tagText = 'Tag: ' + serialNumber;
711
+ for (let record of message.records) {
712
+ if (record.recordType === 'text' || record.recordType === 'url') {
713
+ tagText += '\n' + record.data;
714
+ }
715
+ }
716
+ }
717
+ ```
718
+
719
+ **Record Types:**
720
+
721
+ NFC tags contain NDEF records. The most common types are:
722
+
723
+ | Record Type | `record.data` Contains | Example |
724
+ |-------------|----------------------|----------|
725
+ | `text` | Decoded string | `"Hello World"` |
726
+ | `url` | Decoded URL string | `"https://example.com"` |
727
+ | `mime` | Decoded string or parsed JSON | `{ id: 42 }` |
728
+ | other | Raw `DataView` | Binary data |
729
+
730
+ **Best Practices:**
731
+ - Always check `window.nfcEnabled` before relying on NFC features
732
+ - Use the `nfcRead()` callback for real-time tag processing
733
+ - Use `window.lastNfcMessage` in `draw()` for displaying the most recent tag
734
+ - Test on Android devices with Chrome — NFC is not available on iOS or desktop browsers
735
+ - Tags must be NDEF-formatted to be read by the Web NFC API
736
+
616
737
  ### Speech Recognition Activation
617
738
 
618
739
  **Purpose:** Enable the Web Speech API for voice input and speech-to-text in mobile browsers.
@@ -899,6 +1020,7 @@ The canvas displays a centered message until the user taps. Great for "full-scre
899
1020
  - `enableSensorCanvas(message)`
900
1021
  - `enableMicCanvas(message)`
901
1022
  - `enableSpeechCanvas(message)`
1023
+ - `enableNfcCanvas(message)`
902
1024
  - `enableAllCanvas(message)`
903
1025
  - `enableCameraCanvas(message)`
904
1026
 
@@ -925,6 +1047,7 @@ A styled banner slides in from the top of the screen with an animated entrance.
925
1047
  - `enableSensorBanner(message)`
926
1048
  - `enableMicBanner(message)`
927
1049
  - `enableSpeechBanner(message)`
1050
+ - `enableNfcBanner(message)`
928
1051
  - `enableAllBanner(message)`
929
1052
  - `enableCameraBanner(message)`
930
1053
 
@@ -952,6 +1075,7 @@ Bind the permission activation to any existing HTML element on the page using a
952
1075
  - `enableSensorOn(selector)`
953
1076
  - `enableMicOn(selector)`
954
1077
  - `enableSpeechOn(selector)`
1078
+ - `enableNfcOn(selector)`
955
1079
  - `enableAllOn(selector)`
956
1080
  - `enableCameraOn(selector)`
957
1081
 
package/dist/p5-phone.js CHANGED
@@ -87,9 +87,20 @@ window.soundEnabled = false;
87
87
  window.gesturesLocked = false;
88
88
  window.vibrationEnabled = false;
89
89
  window.speechEnabled = false;
90
+ window.nfcEnabled = false;
91
+ window.lastNfcMessage = null;
92
+ window.lastNfcSerialNumber = null;
90
93
 
91
94
  // Internal state
92
95
  let _micInstance = null;
96
+ let _nfcReader = null;
97
+ let _nfcAbortController = null;
98
+
99
+ // p5.js version detection (1.x vs 2.x)
100
+ const _p5MajorVersion = (typeof p5 !== 'undefined' && p5.VERSION)
101
+ ? parseInt(p5.VERSION.split('.')[0], 10)
102
+ : 1; // Default to 1 if p5 not loaded yet
103
+ const _isP5v2 = _p5MajorVersion >= 2;
93
104
 
94
105
  // =========================================
95
106
  // PUBLIC API - CALL THESE FROM YOUR P5 SKETCH
@@ -229,6 +240,30 @@ function enableVibrationTap(message = 'Tap screen to enable vibration') {
229
240
  });
230
241
  }
231
242
 
243
+ /**
244
+ * Enable NFC tag reading with a button interface
245
+ * Creates a start button that user must click
246
+ * Note: Web NFC is supported on Android Chrome 89+ only, not iOS
247
+ */
248
+ function enableNfcButton(buttonText = 'ENABLE NFC', statusText = 'Enabling NFC...') {
249
+ _createPermissionButton(buttonText, statusText, async () => {
250
+ await _requestNfcPermission();
251
+ console.log('✅ NFC enabled via button');
252
+ });
253
+ }
254
+
255
+ /**
256
+ * Enable NFC tag reading with tap-to-start
257
+ * User taps anywhere on screen to enable
258
+ * Note: Web NFC is supported on Android Chrome 89+ only, not iOS
259
+ */
260
+ function enableNfcTap(message = 'Tap screen to enable NFC') {
261
+ _createTapToEnable(message, async () => {
262
+ await _requestNfcPermission();
263
+ console.log('✅ NFC enabled via tap');
264
+ });
265
+ }
266
+
232
267
  /**
233
268
  * Enable both motion sensors and microphone with a button interface
234
269
  * Creates a start button that user must click to enable both
@@ -312,6 +347,16 @@ function enableVibrationCanvas(message = 'Touch to start') {
312
347
  });
313
348
  }
314
349
 
350
+ /**
351
+ * Enable NFC on first canvas touch
352
+ */
353
+ function enableNfcCanvas(message = 'Touch to start') {
354
+ _createCanvasToEnable(message, async () => {
355
+ await _requestNfcPermission();
356
+ console.log('✅ NFC enabled via canvas touch');
357
+ });
358
+ }
359
+
315
360
  /**
316
361
  * Enable both motion sensors and microphone on first canvas touch
317
362
  */
@@ -379,6 +424,13 @@ function enableVibrationBanner(message = 'Tap to enable vibration', position = '
379
424
  });
380
425
  }
381
426
 
427
+ function enableNfcBanner(message = 'Tap to enable NFC', position = 'top') {
428
+ _createBannerToEnable(message, position, async () => {
429
+ await _requestNfcPermission();
430
+ console.log('✅ NFC enabled via banner');
431
+ });
432
+ }
433
+
382
434
  function enableAllBanner(message = 'Tap to enable sensors & microphone', position = 'top') {
383
435
  _createBannerToEnable(message, position, async () => {
384
436
  await _requestMotionPermissionsCore();
@@ -439,6 +491,13 @@ function enableVibrationOn(selector) {
439
491
  });
440
492
  }
441
493
 
494
+ function enableNfcOn(selector) {
495
+ _bindPermissionTo(selector, async () => {
496
+ await _requestNfcPermission();
497
+ console.log('✅ NFC enabled via custom element');
498
+ });
499
+ }
500
+
442
501
  function enableAllOn(selector) {
443
502
  _bindPermissionTo(selector, async () => {
444
503
  await _requestMotionPermissionsCore();
@@ -488,6 +547,19 @@ function stopVibration() {
488
547
  }
489
548
  }
490
549
 
550
+ /**
551
+ * Stop NFC scanning
552
+ */
553
+ function stopNfc() {
554
+ if (_nfcAbortController) {
555
+ _nfcAbortController.abort();
556
+ _nfcAbortController = null;
557
+ }
558
+ _nfcReader = null;
559
+ window.nfcEnabled = false;
560
+ console.log('NFC scanning stopped');
561
+ }
562
+
491
563
  // =========================================
492
564
  // INTERNAL PERMISSION HANDLERS
493
565
  // =========================================
@@ -613,6 +685,102 @@ async function _requestVibrationPermissionCore() {
613
685
  }
614
686
  }
615
687
 
688
+ async function _requestNfcPermissionCore() {
689
+ try {
690
+ // Check if Web NFC API is supported
691
+ if (!('NDEFReader' in window)) {
692
+ console.warn('⚠️ Web NFC API not supported on this device/browser (Android Chrome 89+ required)');
693
+ if (_debugVisible) {
694
+ debugWarn('Web NFC not supported on this device/browser');
695
+ }
696
+ window.nfcEnabled = false;
697
+ return;
698
+ }
699
+
700
+ _nfcAbortController = new AbortController();
701
+ _nfcReader = new NDEFReader();
702
+
703
+ _nfcReader.onreading = (event) => {
704
+ const serialNumber = event.serialNumber || '';
705
+ const decoder = new TextDecoder();
706
+ const records = [];
707
+
708
+ for (const record of event.message.records) {
709
+ const entry = {
710
+ recordType: record.recordType,
711
+ mediaType: record.mediaType || null,
712
+ id: record.id || null,
713
+ data: null,
714
+ raw: record.data
715
+ };
716
+
717
+ if (record.recordType === 'text' || record.recordType === 'url') {
718
+ entry.data = decoder.decode(record.data);
719
+ } else if (record.recordType === 'mime' && record.mediaType) {
720
+ try {
721
+ const text = decoder.decode(record.data);
722
+ if (record.mediaType.includes('json')) {
723
+ entry.data = JSON.parse(text);
724
+ } else {
725
+ entry.data = text;
726
+ }
727
+ } catch (e) {
728
+ entry.data = record.data;
729
+ }
730
+ } else {
731
+ entry.data = record.data;
732
+ }
733
+
734
+ records.push(entry);
735
+ }
736
+
737
+ const message = { serialNumber: serialNumber, records: records };
738
+ window.lastNfcMessage = message;
739
+ window.lastNfcSerialNumber = serialNumber;
740
+
741
+ // Call user-defined callback if it exists
742
+ if (typeof nfcRead === 'function') {
743
+ nfcRead(message, serialNumber);
744
+ }
745
+
746
+ console.log('NFC tag read — serial:', serialNumber, 'records:', records.length);
747
+ if (_debugVisible) {
748
+ debug('NFC tag read: ' + serialNumber);
749
+ }
750
+ };
751
+
752
+ _nfcReader.onreadingerror = (event) => {
753
+ console.warn('⚠️ NFC read error — tag may be incompatible or out of range');
754
+ if (_debugVisible) {
755
+ debugWarn('NFC read error — tag incompatible or out of range');
756
+ }
757
+ };
758
+
759
+ await _nfcReader.scan({ signal: _nfcAbortController.signal });
760
+ window.nfcEnabled = true;
761
+ console.log('✅ NFC scanning active');
762
+
763
+ } catch (error) {
764
+ if (error.name === 'NotAllowedError') {
765
+ console.warn('⚠️ NFC permission denied by user');
766
+ if (_debugVisible) {
767
+ debugWarn('NFC permission denied');
768
+ }
769
+ } else if (error.name === 'NotSupportedError') {
770
+ console.warn('⚠️ NFC not supported on this device');
771
+ if (_debugVisible) {
772
+ debugWarn('NFC not supported on this device');
773
+ }
774
+ } else {
775
+ console.error('NFC permission error:', error);
776
+ if (_debugVisible) {
777
+ debugError('NFC error: ' + error.message);
778
+ }
779
+ }
780
+ window.nfcEnabled = false;
781
+ }
782
+ }
783
+
616
784
  // Wrapped versions that notify the sketch (used by single-permission functions)
617
785
  async function _requestMotionPermissions() {
618
786
  await _requestMotionPermissionsCore();
@@ -639,6 +807,11 @@ async function _requestVibrationPermission() {
639
807
  _notifySketchReady();
640
808
  }
641
809
 
810
+ async function _requestNfcPermission() {
811
+ await _requestNfcPermissionCore();
812
+ _notifySketchReady();
813
+ }
814
+
642
815
  function _notifySketchReady() {
643
816
  // Call userSetupComplete if it exists
644
817
  if (typeof userSetupComplete === 'function') {
@@ -653,6 +826,7 @@ function _notifySketchReady() {
653
826
  sound: window.soundEnabled,
654
827
  speech: window.speechEnabled,
655
828
  vibration: window.vibrationEnabled,
829
+ nfc: window.nfcEnabled,
656
830
  gestures: window.gesturesLocked
657
831
  }
658
832
  }));
@@ -1141,29 +1315,37 @@ function _initializeP5TouchOverrides() {
1141
1315
  }
1142
1316
 
1143
1317
  function _overrideP5Touch() {
1144
- const origTouchStarted = window.touchStarted || function() {};
1145
- const origTouchMoved = window.touchMoved || function() {};
1146
- const origTouchEnded = window.touchEnded || function() {};
1147
1318
  const origMousePressed = window.mousePressed || function() {};
1148
1319
  const origMouseDragged = window.mouseDragged || function() {};
1149
1320
  const origMouseReleased = window.mouseReleased || function() {};
1150
1321
 
1151
- // Ensure all touch functions return false to prevent default behaviors
1152
- window.touchStarted = function(e) {
1153
- origTouchStarted(e);
1154
- return false;
1155
- };
1156
-
1157
- window.touchMoved = function(e) {
1158
- origTouchMoved(e);
1159
- return false;
1160
- };
1161
-
1162
- window.touchEnded = function(e) {
1163
- origTouchEnded(e);
1164
- return false;
1165
- };
1322
+ // In p5.js 2.0, touch and mouse are unified via Pointer API.
1323
+ // mousePressed/mouseDragged/mouseReleased fire for ALL pointer types (mouse + touch).
1324
+ // In p5.js 1.x, touchStarted/touchMoved/touchEnded are separate from mouse callbacks.
1325
+ // We wrap both sets for 1.x, and only mouse callbacks for 2.0.
1326
+ if (!_isP5v2) {
1327
+ // p5.js 1.x: also wrap touch-specific callbacks
1328
+ const origTouchStarted = window.touchStarted || function() {};
1329
+ const origTouchMoved = window.touchMoved || function() {};
1330
+ const origTouchEnded = window.touchEnded || function() {};
1331
+
1332
+ window.touchStarted = function(e) {
1333
+ origTouchStarted(e);
1334
+ return false;
1335
+ };
1336
+
1337
+ window.touchMoved = function(e) {
1338
+ origTouchMoved(e);
1339
+ return false;
1340
+ };
1341
+
1342
+ window.touchEnded = function(e) {
1343
+ origTouchEnded(e);
1344
+ return false;
1345
+ };
1346
+ }
1166
1347
 
1348
+ // Mouse callbacks — work in both 1.x and 2.0
1167
1349
  window.mousePressed = function(e) {
1168
1350
  origMousePressed(e);
1169
1351
  return false;
@@ -1389,6 +1571,9 @@ window.enableVibrationTap = enableVibrationTap;
1389
1571
  window.enableVibrationButton = enableVibrationButton;
1390
1572
  window.vibrate = vibrate;
1391
1573
  window.stopVibration = stopVibration;
1574
+ window.enableNfcTap = enableNfcTap;
1575
+ window.enableNfcButton = enableNfcButton;
1576
+ window.stopNfc = stopNfc;
1392
1577
  window.enableAllTap = enableAllTap;
1393
1578
  window.enableAllButton = enableAllButton;
1394
1579
 
@@ -1398,6 +1583,7 @@ window.enableMicCanvas = enableMicCanvas;
1398
1583
  window.enableSoundCanvas = enableSoundCanvas;
1399
1584
  window.enableSpeechCanvas = enableSpeechCanvas;
1400
1585
  window.enableVibrationCanvas = enableVibrationCanvas;
1586
+ window.enableNfcCanvas = enableNfcCanvas;
1401
1587
  window.enableAllCanvas = enableAllCanvas;
1402
1588
  window.enableCameraCanvas = enableCameraCanvas;
1403
1589
 
@@ -1407,6 +1593,7 @@ window.enableMicBanner = enableMicBanner;
1407
1593
  window.enableSoundBanner = enableSoundBanner;
1408
1594
  window.enableSpeechBanner = enableSpeechBanner;
1409
1595
  window.enableVibrationBanner = enableVibrationBanner;
1596
+ window.enableNfcBanner = enableNfcBanner;
1410
1597
  window.enableAllBanner = enableAllBanner;
1411
1598
  window.enableCameraBanner = enableCameraBanner;
1412
1599
 
@@ -1416,6 +1603,7 @@ window.enableMicOn = enableMicOn;
1416
1603
  window.enableSoundOn = enableSoundOn;
1417
1604
  window.enableSpeechOn = enableSpeechOn;
1418
1605
  window.enableVibrationOn = enableVibrationOn;
1606
+ window.enableNfcOn = enableNfcOn;
1419
1607
  window.enableAllOn = enableAllOn;
1420
1608
  window.enableCameraOn = enableCameraOn;
1421
1609
 
@@ -2171,6 +2359,9 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
2171
2359
  p5.prototype.enableVibrationButton = enableVibrationButton;
2172
2360
  p5.prototype.vibrate = vibrate;
2173
2361
  p5.prototype.stopVibration = stopVibration;
2362
+ p5.prototype.enableNfcTap = enableNfcTap;
2363
+ p5.prototype.enableNfcButton = enableNfcButton;
2364
+ p5.prototype.stopNfc = stopNfc;
2174
2365
  p5.prototype.enableAllTap = enableAllTap;
2175
2366
  p5.prototype.enableAllButton = enableAllButton;
2176
2367
 
@@ -2180,6 +2371,7 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
2180
2371
  p5.prototype.enableSoundCanvas = enableSoundCanvas;
2181
2372
  p5.prototype.enableSpeechCanvas = enableSpeechCanvas;
2182
2373
  p5.prototype.enableVibrationCanvas = enableVibrationCanvas;
2374
+ p5.prototype.enableNfcCanvas = enableNfcCanvas;
2183
2375
  p5.prototype.enableAllCanvas = enableAllCanvas;
2184
2376
  p5.prototype.enableCameraCanvas = enableCameraCanvas;
2185
2377
 
@@ -2189,6 +2381,7 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
2189
2381
  p5.prototype.enableSoundBanner = enableSoundBanner;
2190
2382
  p5.prototype.enableSpeechBanner = enableSpeechBanner;
2191
2383
  p5.prototype.enableVibrationBanner = enableVibrationBanner;
2384
+ p5.prototype.enableNfcBanner = enableNfcBanner;
2192
2385
  p5.prototype.enableAllBanner = enableAllBanner;
2193
2386
  p5.prototype.enableCameraBanner = enableCameraBanner;
2194
2387
 
@@ -2198,6 +2391,7 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
2198
2391
  p5.prototype.enableSoundOn = enableSoundOn;
2199
2392
  p5.prototype.enableSpeechOn = enableSpeechOn;
2200
2393
  p5.prototype.enableVibrationOn = enableVibrationOn;
2394
+ p5.prototype.enableNfcOn = enableNfcOn;
2201
2395
  p5.prototype.enableAllOn = enableAllOn;
2202
2396
  p5.prototype.enableCameraOn = enableCameraOn;
2203
2397
 
@@ -2215,4 +2409,83 @@ if (typeof p5 !== 'undefined' && p5.prototype) {
2215
2409
  p5.prototype.debugWarn = debugWarn;
2216
2410
 
2217
2411
  console.log('✅ Mobile p5.js Permissions: p5.prototype functions registered');
2412
+ }
2413
+
2414
+ // =========================================
2415
+ // P5.JS 2.0 ADDON REGISTRATION
2416
+ // =========================================
2417
+
2418
+ /**
2419
+ * Register as a p5.js 2.0 addon via p5.registerAddon().
2420
+ * This provides the modern lifecycle integration for p5.js 2.0+
2421
+ * while the p5.prototype block above handles 1.x compatibility.
2422
+ */
2423
+ if (typeof p5 !== 'undefined' && typeof p5.registerAddon === 'function') {
2424
+ p5.registerAddon(function(p5, fn, lifecycles) {
2425
+ // Register all public functions on the prototype (fn === p5.prototype)
2426
+ // Core permission functions
2427
+ fn.lockGestures = lockGestures;
2428
+ fn.enableGyroTap = enableGyroTap;
2429
+ fn.enableGyroButton = enableGyroButton;
2430
+ fn.enableMicTap = enableMicTap;
2431
+ fn.enableMicButton = enableMicButton;
2432
+ fn.enableSoundTap = enableSoundTap;
2433
+ fn.enableSoundButton = enableSoundButton;
2434
+ fn.enableSpeechTap = enableSpeechTap;
2435
+ fn.enableSpeechButton = enableSpeechButton;
2436
+ fn.enableVibrationTap = enableVibrationTap;
2437
+ fn.enableVibrationButton = enableVibrationButton;
2438
+ fn.vibrate = vibrate;
2439
+ fn.stopVibration = stopVibration;
2440
+ fn.enableNfcTap = enableNfcTap;
2441
+ fn.enableNfcButton = enableNfcButton;
2442
+ fn.stopNfc = stopNfc;
2443
+ fn.enableAllTap = enableAllTap;
2444
+ fn.enableAllButton = enableAllButton;
2445
+
2446
+ // Canvas-first-touch style
2447
+ fn.enableGyroCanvas = enableGyroCanvas;
2448
+ fn.enableMicCanvas = enableMicCanvas;
2449
+ fn.enableSoundCanvas = enableSoundCanvas;
2450
+ fn.enableSpeechCanvas = enableSpeechCanvas;
2451
+ fn.enableVibrationCanvas = enableVibrationCanvas;
2452
+ fn.enableNfcCanvas = enableNfcCanvas;
2453
+ fn.enableAllCanvas = enableAllCanvas;
2454
+ fn.enableCameraCanvas = enableCameraCanvas;
2455
+
2456
+ // Banner style
2457
+ fn.enableGyroBanner = enableGyroBanner;
2458
+ fn.enableMicBanner = enableMicBanner;
2459
+ fn.enableSoundBanner = enableSoundBanner;
2460
+ fn.enableSpeechBanner = enableSpeechBanner;
2461
+ fn.enableVibrationBanner = enableVibrationBanner;
2462
+ fn.enableNfcBanner = enableNfcBanner;
2463
+ fn.enableAllBanner = enableAllBanner;
2464
+ fn.enableCameraBanner = enableCameraBanner;
2465
+
2466
+ // Custom element binding
2467
+ fn.enableGyroOn = enableGyroOn;
2468
+ fn.enableMicOn = enableMicOn;
2469
+ fn.enableSoundOn = enableSoundOn;
2470
+ fn.enableSpeechOn = enableSpeechOn;
2471
+ fn.enableVibrationOn = enableVibrationOn;
2472
+ fn.enableNfcOn = enableNfcOn;
2473
+ fn.enableAllOn = enableAllOn;
2474
+ fn.enableCameraOn = enableCameraOn;
2475
+
2476
+ // Camera functions
2477
+ fn.createPhoneCamera = createPhoneCamera;
2478
+ fn.enableCameraButton = enableCameraButton;
2479
+ fn.enableCameraTap = enableCameraTap;
2480
+
2481
+ // Debug functions
2482
+ fn.showDebug = showDebug;
2483
+ fn.hideDebug = hideDebug;
2484
+ fn.toggleDebug = toggleDebug;
2485
+ fn.debug = debug;
2486
+ fn.debugError = debugError;
2487
+ fn.debugWarn = debugWarn;
2488
+
2489
+ console.log('✅ Mobile p5.js Permissions: registered as p5.js 2.0 addon');
2490
+ });
2218
2491
  }