DEV Community

ekko1500
ekko1500

Posted on

Problem: Scratch Extension Cannot Communicate with scratch-gui / Web Bluetooth Situation

I was developing a custom Scratch extension:

kankaung-extension-bt.js
inside a modified scratch-gui.

Goal:

Scratch Blocks
↓
KanKaung Extension
↓
Web Bluetooth
↓
ESP32 BLE
The ESP32 BLE connection worked when testing directly in Chrome:

navigator.bluetooth.requestDevice()
but when running from Scratch blocks, it failed.

Problem 1: Web Bluetooth worked in console but not from Scratch block
Error
Permissions policy violation:
bluetooth is not allowed in this document
or:

SecurityError:
Failed to execute requestDevice()
Cause
Scratch extensions are not running in the main browser window.

The extension is loaded inside an iframe:

window === top
false
Meaning:

Browser Window
│
├── scratch-gui
│
└── Extension iframe
|
└── kankaung-extension-bt.js
The iframe has different permissions and cannot directly access Web Bluetooth.

Problem 2: Trying to access Scratch VM
I tried:

Scratch.vm
or:

Scratch.runtime
Result:

undefined
Cause
External extensions only receive the public Scratch API:

Scratch.BlockType
Scratch.ArgumentType
Scratch.extensions.register()
They do NOT receive:

vm
runtime
redux store
Wrong Approach
Trying:

window.dispatchEvent(
new CustomEvent("KANKAUNG_CONNECT")
)
and listening in:

scratch-gui
did not work.

Why?
Because events stay inside the iframe.

Extension iframe

window.dispatchEvent()
|
X
|
scratch-gui window
The two windows are separated.

Wrong Approach 2
Trying:

window.parent.dispatchEvent()
or:

window.top.dispatchEvent()
caused:

Blocked a frame with origin "null"
from accessing a cross-origin frame
Cause
Browser security prevents direct access between different origins.

Even localhost pages can become:

origin: null
when loaded through iframe sandboxing.

Final Solution: Use postMessage()
postMessage() is designed for iframe communication.

Architecture:

Scratch Extension iframe
|
|
| window.parent.postMessage()
|
↓
scratch-gui
containers/gui.jsx
|
|
| Web Bluetooth API
|
↓
ESP32 BLE
Extension → scratch-gui
Example:

window.parent.postMessage(
{
type:"KANKAUNG_SEND",
command:"LED_ON"
},
"*");
scratch-gui receives message
In:

src/containers/gui.jsx
listen:

window.addEventListener(
"message",
(event)=>{

if(event.data.type==="KANKAUNG_SEND")
{
    kankaungBluetooth.send(
        event.data.command
    );
}
Enter fullscreen mode Exit fullscreen mode

});
scratch-gui → Extension
Do NOT use:

dispatchEvent()
Use:

window.postMessage(
{
type:"KANKAUNG_STATUS",
status:true
},
"*");
Extension listens:

window.addEventListener(
"message",
(event)=>{

if(event.data.type==="KANKAUNG_STATUS")
{
    this.connected =
    event.data.status;
}
Enter fullscreen mode Exit fullscreen mode

});
Final Communication Flow
User clicks Scratch block

    ↓
Enter fullscreen mode Exit fullscreen mode

kankaung-extension-bt.js

    ↓
Enter fullscreen mode Exit fullscreen mode

postMessage()

    ↓
Enter fullscreen mode Exit fullscreen mode

src/containers/gui.jsx

    ↓
Enter fullscreen mode Exit fullscreen mode

kankaung-bluetooth.js

    ↓
Enter fullscreen mode Exit fullscreen mode

navigator.bluetooth

    ↓
Enter fullscreen mode Exit fullscreen mode

ESP32 BLE

    ↓
Enter fullscreen mode Exit fullscreen mode

response

    ↓
Enter fullscreen mode Exit fullscreen mode

postMessage()

    ↓
Enter fullscreen mode Exit fullscreen mode

Scratch Extension
Key Lessons To Remember

  1. Scratch extension ≠ scratch-gui Extension:

iframe
GUI:

main application
They are separated.

  1. Never use:
    Scratch.vm
    Scratch.runtime
    window.dispatchEvent()
    window.parent.dispatchEvent()
    for communication between extension and scratch-gui.

  2. Use:
    window.parent.postMessage()
    Extension → GUI

and:

window.postMessage()
GUI → Extension

  1. For custom hardware extensions in scratch-gui: Recommended architecture:

Extension
|
| postMessage
|
scratch-gui container
|
| Hardware manager
|
ESP32 / Arduino / Raspberry Pi
This approach is similar to how professional Scratch forks and hardware extensions communicate. You solved the hardest part: getting a custom Scratch extension to talk to your own hardware layer without Scratch Link.

Top comments (0)