How to Embed JavaScript and HTML in Google AI Edge Gallery Agent Skills

Agent Skills in the Google AI Edge Gallery are self-contained web applications that expose a global window['ai_edge_gallery_get_result'] function to bridge LLM tool calls with interactive HTML, CSS, and JavaScript interfaces running inside a native webview.

Agent Skills in the Google AI Edge Gallery embed JavaScript and HTML to create on-device interactive experiences that run inside a native webview. By implementing a minimal JavaScript bridge and bundling standard web assets, developers can deploy complex interfaces—from audio synthesizers to data visualizations—without writing native code. This architecture keeps all execution local while providing full access to modern browser capabilities.

The Three-Component Architecture

According to the google-ai-edge/gallery source code, every Agent Skill follows a strict three-file bootstrap pattern that separates the entry point, the JavaScript bridge logic, and the user interface.

The Entry Point (index.html)

The index.html file serves as a minimal bootloader that loads the JavaScript entry point. It contains no UI elements, only a script tag pointing to index.js.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My Skill</title>
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>

This file resides in the scripts/ directory alongside index.js, as seen in skills/featured/virtual-piano/scripts/index.html.

The Result Provider (index.js)

The index.js file defines the mandatory global function window['ai_edge_gallery_get_result'] that the Gallery runtime invokes when the LLM calls the skill's run_js tool. This function must return a JSON string containing either a webview object with a url property or an error message.

window['ai_edge_gallery_get_result'] = async (dataStr) => {
  const webviewUrl = `ui.html?v=${Date.now()}`;
  return JSON.stringify({
    webview: { url: webviewUrl },
    result: 'Success. Open the preview card to interact.'
  });
};

The implementation in skills/featured/virtual-piano/scripts/index.js demonstrates appending a timestamp to bust the cache when the skill is re-run.

The Web UI (ui.html)

The actual interface lives in a separate HTML file—typically ui.html or webview.html—referenced by the URL returned from the result provider. This file can use any web technology supported by the device's browser engine, including Web Audio API, Canvas, WebGL, and standard CSS3 layouts.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Simple Tone</title>
  </head>
  <body>
    <button id="play">Play C4</button>
    <script>
      const audio = new Audio('assets/40.mp3');
      document.getElementById('play').onclick = () => {
        audio.currentTime = 0;
        audio.play();
      };
    </script>
  </body>
</html>

This example, based on skills/featured/virtual-piano/assets/ui.html, loads an MP3 from the local assets/ directory using the Web Audio API.

Project Structure and Asset Bundling

Agent Skills require specific directory conventions to ensure the Gallery app packages all dependencies. Static assets such as images, audio files, and additional scripts must live in an assets/ folder referenced with relative paths.

Component Purpose Example Path
index.html Bootstrap loader for the JavaScript bridge skills/featured/virtual-piano/scripts/index.html
index.js Implements ai_edge_gallery_get_result and returns the UI URL skills/featured/virtual-piano/scripts/index.js
ui.html Full interactive interface using HTML, CSS, and JavaScript skills/featured/virtual-piano/assets/ui.html
assets/ Directory containing bundled MP3, PNG, or JS dependencies skills/featured/virtual-piano/assets/
SKILL.md Metadata file declaring the skill name and description skills/featured/virtual-piano/SKILL.md

The Gallery app performs no server-side processing for skill UIs. All code executes on-device, ensuring privacy while maintaining access to the complete web stack.

Declaring the Skill

Each skill requires a SKILL.md file at the root of its directory to register with the Gallery's discovery system. This file contains YAML frontmatter defining the skill's metadata.

---
name: virtual-piano
description: An interactive piano using Web Audio API.
---

See skills/featured/virtual-piano/SKILL.md for the production template used by featured skills.

Summary

  • Agent Skills are self-contained web applications that run inside the Google AI Edge Gallery's native webview without server-side processing.
  • The JavaScript bridge requires implementing window['ai_edge_gallery_get_result'] in index.js to return a JSON string with a webview.url pointing to the UI.
  • The Web UI (ui.html) can leverage any browser technology, including Web Audio API, Canvas, and WebGL, using assets bundled in the local assets/ directory.
  • File structure must follow the convention: scripts/ for bootstrap files, assets/ for static content, and SKILL.md for discovery metadata.

Frequently Asked Questions

What JavaScript APIs are available inside an Agent Skill?

Any API supported by the device's WebView engine is available. The virtual-piano skill demonstrates the Web Audio API for audio playback, while other built-in skills use Canvas for rendering. Since the Gallery uses a full browser engine, standards like WebGL, localStorage, and Fetch API (for local resources) work without polyfills.

Can Agent Skills load external resources from the internet?

No. The architecture is designed for privacy-first, on-device execution. While the WebView supports standard browser capabilities, skills should bundle all assets locally in the assets/ directory and use relative paths (e.g., assets/image.png). The Gallery does not whitelist external domains for skill execution.

The Gallery runtime invokes the global function window['ai_edge_gallery_get_result'] when the LLM calls the skill's run_js tool. This function receives the LLM's input as a string parameter and must return a JSON string containing either a webview object with a url property or an error message. This one-way bridge pattern keeps the contract simple and stateless.

Why does the index.js example append a timestamp to the URL?

The ?v=${Date.now()} pattern implements cache busting. According to the skills/featured/virtual-piano/scripts/index.js implementation, this ensures that when a user re-runs the skill, the Gallery loads a fresh instance of the UI rather than displaying a cached version from the previous invocation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →