Skip to content

Your first Web plugin

Create a text controller for your phone: enter text on a web page and send it to both the Studio virtual screen and the physical glasses.

Before you begin: check tools and directories#

Prepare the complete toolkit, Python and its source-packaging dependencies, Node.js/npm, and desktop Studio. Device testing also requires the companion App and glasses. For your first run, try an existing example; use the links below if your tools are not ready.

Run all terminal commands on this page from the complete plugin-open-platform root directory. If you are still in GlassSDK or PhoneSDK, use cd .. to return to the directory containing both SDKs and Studio.

This example creates an HTML/JavaScript page that runs inside the App and uses GM Web Bridge to display text on the glasses. You do not need to write C first or install Android Studio, Flutter, or Xcode.

Step 1: prepare the directory and SDK files#

Keep the terminal at the toolkit root. The commands below generate the standalone SDK and copy it into a new project. hello-web is not a built-in example. If a project with that name already exists, preserve its files before proceeding.

Open PhoneSDK/examples/hello-web in your code editor and create the following three files using UTF-8 encoding: manifest.json, index.html, and plugin.js. Do not add an extra .txt extension.

macOS / Linux

< / >
node PhoneSDK/tools/sync-example-sdk.mjs
mkdir -p PhoneSDK/examples/hello-web/vendor
cp PhoneSDK/examples/permission-debug/vendor/gm-plugin-web-sdk.esm.js PhoneSDK/examples/hello-web/vendor/

Windows PowerShell

< / >
node PhoneSDK/tools/sync-example-sdk.mjs
New-Item -ItemType Directory -Force PhoneSDK/examples/hello-web/vendor
Copy-Item PhoneSDK/examples/permission-debug/vendor/gm-plugin-web-sdk.esm.js PhoneSDK/examples/hello-web/vendor/

Step 2: create manifest.json#

Save the following to PhoneSDK/examples/hello-web/manifest.json. The current format uses Bridge 2.0 and an array of permission objects; do not use permissions: ["display"] from older documentation.

< / >
{
  "schemaVersion": 2,
  "permissionPolicyVersion": 1,
  "id": "com.example.hello-web",
  "name": "Hello Web",
  "version": "0.1.0",
  "entry": "index.html",
  "bridgeVersion": "2.0",
  "permissions": [
    {
      "name": "display",
      "required": true
    }
  ],
  "deviceRequirements": {
    "preferredPluginId": "com.gm.example.web-bridge",
    "protocols": [
      {
        "id": "gm.scene",
        "minVersion": "1.0"
      }
    ]
  }
}

Step 3: create index.html#

Save to PhoneSDK/examples/hello-web/index.html.

< / >
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Hello Web</title>
</head>
<body>
  <h1>Hello Web</h1>
  <p id="status" role="status">Connecting to MemoMind…</p>
  <label for="message">Text to display</label>
  <input id="message" maxlength="120" value="Hello from Web!">
  <button id="show" disabled>Show text on glasses</button>
  <script type="module" src="./plugin.js"></script>
</body>
</html>

Step 4: create plugin.js#

Save this to PhoneSDK/examples/hello-web/plugin.js. The button becomes available after the SDK handshake succeeds. Text is sent to the glasses only when the user enters nonempty text and clicks the button. This example limits input to 120 characters. Start with the short English text below to verify the font and communication path.

< / >
import { createGMPlugin } from './vendor/gm-plugin-web-sdk.esm.js';

const gm = createGMPlugin();
const status = document.querySelector('#status');
const show = document.querySelector('#show');
const message = document.querySelector('#message');

show.addEventListener('click', async () => {
  const text = message.value.trim();
  if (!text) {
    status.textContent = 'Enter some text first.';
    return;
  }
  show.disabled = true;
  try {
    await gm.display.createPage();
    await gm.display.updateText({
      id: 1, x: 20, y: 20, width: 560, height: 100,
      border: 1, radius: 8, text
    });
    status.textContent = 'Sent. Check the glasses display.';
  } catch (error) {
    status.textContent = `Failed: ${error.code ?? ''} ${error.message ?? error}`;
  } finally {
    show.disabled = false;
  }
});

gm.ready().then(() => {
  status.textContent = 'Ready. Press the button.';
  show.disabled = false;
}).catch(error => {
  status.textContent = `Startup failed: ${error.message ?? error}`;
});

Before continuing: check the locations of all four files#

Save all four files before building. On Windows, enable “File name extensions” in File Explorer to check for names such as manifest.json.txt. Save each code block to its corresponding file without the Markdown backticks.

Directory structure
PhoneSDK/examples/hello-web/
├── manifest.json
├── index.html
├── plugin.js
└── vendor/
    └── gm-plugin-web-sdk.esm.js

Step 5: automatically build and package the Web plugin#

From the complete toolkit root, run the command below. The script discovers hello-web and generates PhoneSDK/dist/hello-web-0.1.0.mmpkg. Run the same command after future Web code changes.

This is a static web page, so the script packages it directly. For projects with a build script in package.json, it also installs dependencies as needed, runs the frontend build, and packages dist. You do not need to write a separate node packaging command each time.

macOS / Linux

< / >
./build.py web

Windows PowerShell

< / >
py build.py web

Step 6: prepare the glasses bridge plugin#

From the complete toolkit root, the following command builds the glasses plugins, including GM Web Bridge for this example. You do not need to edit its C source. Existing build outputs are reused when nothing has changed.

After a successful build, confirm that GlassSDK/build-host/.build/web_bridge/ contains web_bridge.gmp, web_bridge.review.json and web_bridge.review-source.enc. Keep all three files together.

macOS / Linux

< / >
./build.py glass

Windows PowerShell

< / >
py build.py glass

Why you should not double-click index.html#

gm.ready() requires a handshake from a Studio or App host. Opening the HTML file directly or using an ordinary development server lets you preview the static layout, but the button may keep waiting for a connection.

The App prepares the Web session and starts the glasses GMP in parallel. Call gm.ready() early; do not make the handshake depend on a reply from the glasses or a business network response. Communication is gated until the glasses are ready. This tutorial sends display commands only after the user clicks the button.

Prepare in parallel; communicate when readyThe App prepares the Web session and glasses GMP in parallel. Business communication and runtime results become available after the Web page completes gm.ready() and the glasses confirm startup. A cache hit avoids retransmitting the GMP.MEMOMIND / WEB STARTUPPrepare in parallel; communicate when readyParallel preparation · Both paths advancePhone Web sessionPrewarm / create runtimeComplete gm.ready() earlyGlasses GMPHit: Verify and startMiss: transfer, then startBoth ready · Enable business communicationConfirm runtime resultsWeb ready and glasses startedClean up both on cancel / failureOpen the business pageThen send display or plugincommandsUpdate state from repliesgm.ready() only confirms the Web–host handshake. Do not wait for glasses replies orbusiness-network responses before calling ready.
Prepare in parallel; communicate when readyThe App prepares the Web session and glasses GMP in parallel. Business communication and runtime results become available after the Web page completes gm.ready() and the glasses confirm startup. A cache hit avoids retransmitting the GMP.MEMOMIND / WEB STARTUPPrepare in parallel;communicate when readyParallel preparation · Bothpaths advancePhone Web sessionPrewarm / create runtimeComplete gm.ready() earlyGlasses GMPHit: Verify and startMiss: transfer, then startBoth ready · Enablebusiness communicationConfirm runtime resultsWeb ready and glassesstartedClean up both on cancel /failureOpen the business pageThen send display or plugincommandsUpdate state from repliesgm.ready() only confirms the Web–host handshake. Do not wait forglasses replies or business-networkresponses before calling ready.
Web ready is one step in startup; it does not mean the glasses application is ready.

Step 7: verify the Web page and glasses display in Studio#

  1. Open desktop Studio and import the complete toolkit root containing GlassSDK, PhoneSDK and Studio. On a Mac, import it after each fresh launch. Use the installation link below if Studio is not installed.
  2. Refresh and select Hello Web on the phone side and GM Web Bridge on the glasses side. Do not leave My Glass or Breakout selected from a previous run.
  3. When the page shows Ready. Press the button., leave the default input unchanged and click Show text on glasses. The virtual glasses should display Hello from Web!.
  4. Change the input to My phone controls glasses and send it again. The virtual glasses should display the new text. Clear the input and click again to check for Enter some text first.
  5. If the button stays disabled, check the Web log and SDK file path. If sending fails, keep the error message and first check the selected plugins, permissions, and bridge GMP.

Step 8: scan the QR code and tap the same button on your phone#

  1. Confirm that MemoMind App shows the glasses as connected over Bluetooth and that the computer and phone can reach each other on the LAN. Keep Hello Web + GM Web Bridge selected in Studio and wait for the current ZIP and QR code.
  2. Sign in to the App with the developer account that owns the application, then open “Scan to open application”. Grant the required camera/local network permissions, approve display access after downloading, and wait for installation and startup to finish.
  3. After Hello Web opens on the phone, click Show text on glasses. The physical glasses should display Hello from Web!. Then enter and send My phone controls glasses and confirm that the glasses update.
  4. Stop this local entry in the App and start it again. Confirm that the page can complete a new handshake and send text. This example does not save the input, so reopening it with the default text is expected.

Step 9: change the default content and version, then scan again#

  1. In PhoneSDK/examples/hello-web/index.html, change the input value from Hello from Web! to My first plugin!. In manifest.json in the same directory, change version from 0.1.0 to 0.1.1. Save both files.
  2. From the toolkit root, run the command below and confirm that PhoneSDK/dist/hello-web-0.1.1.mmpkg is generated. GM Web Bridge has not changed, so you do not need to rewrite the glasses source.
  3. Refresh Studio and select Hello Web, keeping GM Web Bridge selected for the glasses. Verify the new default input and the updated virtual screen after sending.
  4. Wait for the current ZIP to finish packaging. Stop the old entry in the App, scan again, and open the new page. Verify the updated default text, then send it to the physical glasses.
  5. Close Studio on the computer, then start the saved entry in the App again. Text input and sending should still work while the phone remains connected to the glasses.

macOS / Linux

< / >
./build.py web

Windows PowerShell

< / >
py build.py web

Where to implement your own application logic#

ObjectiveWhere to editNext
Change the page title, input, button, and layoutindex.html; add CSS if needed and reference it with a relative path.Save, rebuild the Web plugin, and refresh Studio to verify it.
Change click behavior or the data sentThe click callback in plugin.js.Keep ready before business calls, and handle empty input, waiting, and errors.
Add saved data, files, location, or recordingplugin.js and the corresponding permissions in manifest.json.Check the capability and required versions first, then copy a small example. For recording, also check the current audio contract.
Run your own game, protocol, or computation on the glassesCreate your own GMP and pair it using deviceRequirements/provides.Continue with “Paired debugging”. Standard text display can continue to use GM Web Bridge.

Troubleshoot by symptom#

SymptomCheck first.
A file or tool cannot be foundCheck that the terminal is at the toolkit root, node/python commands work, and all four project files have been saved.
Hello Web is missing from StudioConfirm that hello-web-0.1.0.mmpkg exists. Refresh the root workspace, or use Import package to import the MMPKG.
The page stays on ConnectingOpen it inside Studio/App and check the plugin.js and vendor paths. Do not just double-click index.html.
The page shows Enter some text first.Enter some text and click again. This is the example's empty-input check.
The page works but the glasses do not updateCheck that the GMP is GM Web Bridge. Keep the send error and confirm that the physical glasses are connected and the plugin has started.
The old page still appears after an updateConfirm that you edited the current project, rebuilt it, and refreshed Studio. Stop the old entry in the App and scan again; refreshing the old phone page is not enough.

Reference files in the toolkit#

  • PhoneSDK/packages/web-sdk/src/index.js
  • PhoneSDK/packages/web-sdk/src/index.d.ts
  • PhoneSDK/packages/bridge-contract/src/permission-policy.js
  • PhoneSDK/examples/permission-debug/plugin.js