Skip to content

Music for a tile ​

This skill requires the generate_music tool. If it is unavailable, explain that music generation is unavailable in this session. Never ask for provider keys, install a provider SDK, or call a provider directly. Generation happens during creation; players only play the saved asset.

Generate once ​

  1. Find the tile directory and inspect its existing audio assets and playback code. Reuse a suitable saved track on subsequent edits. Regenerate only when requested, the asset is missing or broken, or the requested soundtrack has materially changed.
  2. Describe mood, instruments, tempo, and intended use. Prefer a short 15–30 second instrumental for background music. For a loop, request a steady arrangement with a matching beginning and end; generation does not guarantee a seamless join.
  3. Call generate_music once, in sequence:
json
{
  "prompt": "Short atmospheric game soundtrack: warm ambient pads, sparse soft piano, calm exploration, steady texture with a matching beginning and end for looping, no vocals",
  "duration": 20,
  "instrumental": true,
  "outputPath": "my-game/assets/soundtrack.mp3"
}

outputPath is relative to the workspace, including the tile directory. For a root tile use assets/soundtrack.mp3; for a conversion candidate use its staging directory. The tool replaces that file, so use a new filename when preserving an existing soundtrack as a fallback. It accepts 3–120 whole seconds, a prompt of at most 2,000 characters, and MP3 output. instrumental defaults to true; false permits vocals.

Every call costs money and counts toward both the creator's daily model request quota and the separate limit of 100 music requests per UTC day. Do not regenerate just to improve a small detail. After a failure, follow the tool's guidance and tell the creator. A timeout or cancellation may already have been billed: never automatically retry. Keep existing audio when generation fails.

Treat shipped audio as public. Keep private details and credentials out of prompts, filenames and assets.

Load the saved asset ​

Tiles run in a sandboxed frame from a blob: URL; a bare relative media URL does not load there. The platform's CSP blocks data: audio sources and fetch(dataUrl). Decode bundled bytes locally and play a Blob URL.

Prototype: use Bash to write a data-only script beside the saved file, so the audio bytes never pass through model context:

bash
cd <tileDir> && {
  printf 'globalThis.SOUNDTRACK_URL = "data:audio/mpeg;base64,'
  base64 < assets/soundtrack.mp3 | tr -d '\n'
  printf '";\n'
} > assets/soundtrack.js

Load <script src="assets/soundtrack.js"></script> before the main module. The host inlines this data-only script, like generated image data. Pass globalThis.SOUNDTRACK_URL to createMusicUrl below. Keep the original MP3 for future edits and conversion.

Integrated Vite tile: import the file with ?url from the importing module's location, and ensure build.assetsInlineLimit includes the file (10 MiB covers the tool's 4 MiB cap). Add the project's URL import declaration if needed. Pass the imported data URL to createMusicUrl below. Preserve the MP3 when converting a prototype; replace the prototype's data script with the Vite import.

For either recipe, create the URL once per mounted player, then use it as the audio source:

js
function createMusicUrl(dataUrl) {
  const encoded = dataUrl.slice(dataUrl.indexOf(",") + 1);
  const bytes = Uint8Array.from(atob(encoded), (char) => char.charCodeAt(0));
  return URL.createObjectURL(new Blob([bytes], { type: "audio/mpeg" }));
}

Keep this URL for the player's lifetime. On disposal, pause the player, remove its src, call load() to release the media, and call URL.revokeObjectURL(musicUrl). Never revoke it while playback or looping still needs it.

Playback ​

  • Create one audio player per mounted tile. Start it only from a user action such as Play or Enable music. Call audio.play() in that event handler, catch rejection, and leave a visible retry control. Never autoplay on mount or after an async generation completes.
  • Provide a visible, keyboard-accessible mute toggle with a clear current state. Keep that preference local to the player, preserve it across game restarts, and never unmute as a side effect of an edit or game state update.
  • Use a restrained background volume. Loop only background tracks; play stingers and victory cues once. Avoid stacking players when a game restarts.
  • Pause on exit or when hidden; dispose the player and event listeners on unmount. Resume only if the player previously opted in and remains unmuted; handle browser playback rejection.