For designers and client websites

The vibedom widget

A floating button that carries a website's sonic identity. The seed travels inside the snippet: no AI calls, no database and no cost per visitor. Audio only downloads when someone hits play.

1. Paste the snippet

Generate a seed in the playground and copy the code under “Your embeddable widget”. It should look like this, before </body>:

HTML
<!-- vibedom: generative music for your site -->
<script src="https://vibedom.dev/widget.js"
        data-seed="eyJtZXRhZGF0YSI6eyJ1cmwiOiJodHRwczovL25lb24tZ3JpZC5leGFtcGxlIiwiZGV0ZWN0ZWRWaWJlIjoiQ3liZXJwdW5rOiB0w6ljbmljbywgbm9jdHVybm8geSBuZcOzbiIsImV4cGxhbmF0aW9uIjoiRm9uZG8gbmVncm8gcHVybywgYWNlbnRvcyB2ZXJkZSBuZcOzbiB5IHRpcG9ncmFmw61hIG1vbm9zcGFjZSB0cmFkdWNpZG9zIGEgUmUgZnJpZ2lvIGdyYXZlLCBzaWVycmFzIGRlc2FmaW5hZGFzIHRyYXMgdW4gZmlsdHJvIGNlcnJhZG8geSB1biBhcnBlZ2lvIGRlIGVjbyBxdWUgcmVjdWVyZGEgYSBkYXRvcyBmbHV5ZW5kby4ifSwidWlUaGVtZSI6eyJiYWNrZ3JvdW5kQ29sb3IiOiIjMEEwQTBBIiwiYWNjZW50Q29sb3IiOiIjMDBGRjY2IiwidGV4dENvbG9yIjoiI0U2RkZFOSIsImZvbnRGYW1pbHkiOiJtb25vc3BhY2UifSwibXVzaWNhbFBhcmFtcyI6eyJicG0iOjg0LCJyb290Tm90ZSI6IkQiLCJzY2FsZSI6InBocnlnaWFuIiwib2N0YXZlIjozfSwic3ludGhDb25maWciOnsicGFkIjp7IndhdmVmb3JtIjoic2F3dG9vdGgiLCJhdHRhY2siOjIuNSwicmVsZWFzZSI6NCwiZmlsdGVyRnJlcSI6NTIwfSwiYXJwIjp7ImVuYWJsZWQiOnRydWUsIndhdmVmb3JtIjoic3F1YXJlIiwiZGVuc2l0eSI6MC40NSwiZGVsYXlXZXQiOjAuNTV9LCJlZmZlY3RzIjp7InJldmVyYkRlY2F5Ijo1LjUsInJldmVyYldldCI6MC41NSwiY2hvcnVzRGVwdGgiOjAuMjV9LCJpbnRlcmFjdGl2aXR5Ijp7InNjcm9sbFRhcmdldCI6ImZpbHRlckZyZXEiLCJzY3JvbGxNdWx0aXBsaWVyIjozLjUsImhvdmVyTm90ZVRyaWdnZXIiOnRydWV9fX0="
        defer></script>

The seed is the widget. It contains colours, copy and musical parameters. No other service is needed: if vibedom's backend disappeared, this button would keep working.

Webflow, Framer or WordPress

Paste the snippet into the project's custom HTML or the page footer. In WordPress, use a code snippets plugin or the child theme's footer.php. Avoid fields that strip <script> tags.

One widget per page is enough. Keep it out of checkout and admin screens: it works best on the public site, where visitors choose to press play.

2. Attributes

Attribute Required What it does
src Yes Loader URL. Always use https://vibedom.dev/widget.js.
data-seed Yes The Sound Seed in base64, as generated by the playground. If it is missing or corrupt, the widget does not render and never breaks the page.
data-label No Button text and accessible label. Defaults to “Play this website” or “Escuchar esta web” depending on the document's lang.
defer Recommended Does not block the host page's render.

Colours and copy

The button takes backgroundColor, accentColor and textColor from the seed's uiTheme. If accent and background match, it falls back to the text colour so the icon stays visible. To change the palette, regenerate the seed in the playground; there is no CSS to edit.

Example with custom text
<script src="https://vibedom.dev/widget.js"
        data-seed="…"
        data-label="Give this page a soundtrack"
        defer></script>

3. How it works

  1. On page load, the snippet decodes the seed and paints a button inside a Shadow DOM. It does not download the engine or Tone.js. The loader is under 5 KB (around 2 KB compressed).
  2. On the first click, it loads widget-player.js and Tone.js from jsDelivr. That click is also the gesture browsers require to create the audio context.
  3. While playing, the engine performs the seed in the browser: pad, drone and arpeggio. There is no pre-recorded audio and no API request.

The button exposes aria-pressed, a visible focus ring and respects prefers-reduced-motion. Loading and error states are announced through its accessible label, not with an alert that interrupts the visitor.

4. CSP and SRI

If the host site uses a strict Content-Security-Policy, add these origins. The widget needs jsDelivr for Tone.js, blob: for Tone's internal clock and worklets, and inline styles for the Shadow DOM:

Content-Security-Policy
script-src 'self' https://vibedom.dev https://cdn.jsdelivr.net blob:;
worker-src blob:;
style-src 'self' 'unsafe-inline';

Don't paste this over your current CSP. Merge it with what you already have: directives are combined, and a duplicated CSP can disable the more permissive one or break the rest of the site.

Subresource Integrity (SRI)

You can add integrity and crossorigin="anonymous" to the loader. The hash changes every time a new widget.js is published; if you pin it, review the hash when you update. Compute the current one with:

Terminal
curl -s https://vibedom.dev/widget.js \
  | openssl dgst -sha384 -binary \
  | openssl base64 -A

SRI protects the loader; the player and Tone.js are imported at runtime and cannot carry integrity. That is why you should pin the version in the URL as well (see Versioning).

5. Privacy and cost

  • No cookies, localStorage or vibedom analytics.
  • The host page's URL is not sent to any server.
  • Audio is synthesised in the browser; no music is downloaded and nothing is uploaded.
  • The first click requests Tone.js from jsDelivr, which sees the visitor's IP like any CDN. If you need to avoid it, contact us before embedding in a strict environment.
  • Cost per visitor: zero AI calls. The seed is already embedded in the HTML.

6. Troubleshooting

The button does not appear

Check that data-seed is not empty or cut off when copying, and that the CSP allows https://vibedom.dev. If the snippet sits inside an editor that removes <script>, move it to the theme HTML or a code plugin.

The button appears but there is no sound

Audio needs the click gesture: it cannot autoplay. On iOS also check the silent switch. If the CSP blocks blob:, Tone cannot start its internal clock.

The first click takes a second

That is expected: the player and Tone.js (about 230 KB compressed from the CDN) download at that moment. The button shows its loading state in the meantime.

I want the button in another corner or at another size

Position and size are part of the widget's design and are not configurable by attribute. If you need a client-specific variant, regenerate the seed and ask for an adaptation before touching the Shadow DOM's CSS.

Can I put several widgets on the same page?

No. Each snippet creates its own floating button and two buttons overlap. Use one per page.

7. Versioning

To keep an update from silently changing a client's widget, add the version to the URL:

Versioned URL
https://vibedom.dev/widget.js?v=1.0.0

The parameter busts browser and CDN caches, but the file is still the published one. Check the changelog before bumping. Seed changes do not affect the widget: every snippet carries its own.

Ready to try it? Generate a seed from your site and copy the final snippet.

Open the playground