@supernal/tts-widget
v1.0.10
Published
Embeddable TTS widget for blogs and websites - as simple as Google Analytics
Maintainers
Readme
@supernal/tts-widget
Embeddable Text-to-Speech widget for blogs and websites. Add voice to your content in seconds - just like Google Analytics.
Features
✅ Simple as gtag - Drop in two scripts and you're done ✅ Zero JavaScript - Works with plain HTML, no bundlers needed ✅ Auto-discovery - Automatically finds and activates TTS elements ✅ Framework agnostic - Works with React, Vue, vanilla HTML, etc. ✅ Smart defaults - Hardcoded to https://www.tts.supernal.ai, just add your API key
Quick Start (Recommended)
The gtag Way - No npm, No bundler
<!-- In your <head> -->
<link rel="stylesheet" href="https://unpkg.com/@supernal/tts-widget@1/dist/widget.css">
<!-- Before </body> -->
<script src="https://unpkg.com/@supernal/tts-widget@1/dist/widget.js"></script>
<script>
window.SupernalTTS.init({
apiKey: 'YOUR_API_KEY'
});
</script>
<!-- Use anywhere on your page -->
<div class="supernal-tts-widget" data-text="This text will have TTS!">
This text will have TTS!
</div>That's it! The widget automatically scans for .supernal-tts-widget elements when the page loads.
Next.js / React
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/@supernal/tts-widget@1/dist/widget.css" />
</head>
<body>
{children}
<Script src="https://unpkg.com/@supernal/tts-widget@1/dist/widget.js" strategy="afterInteractive" />
<Script id="tts-init" strategy="afterInteractive">
{`
window.SupernalTTS && window.SupernalTTS.init({
apiKey: '${process.env.NEXT_PUBLIC_TTS_API_KEY}'
});
`}
</Script>
</body>
</html>
);
}Then use in your components:
export default function BlogPost({ content }) {
return (
<article className="supernal-tts-widget" data-text={content}>
{content}
</article>
);
}Installation (Optional - for npm workflows)
npm install @supernal/tts-widgetOption 1: Simple Import (No auto-scan)
import { SupernalTTS } from '@supernal/tts-widget';
import '@supernal/tts-widget/widget.css';
SupernalTTS.init({
apiKey: 'YOUR_API_KEY'
});Option 2: React Component
import { TTSInitializer } from '@supernal/tts-widget/react';
import '@supernal/tts-widget/widget.css';
export default function App() {
return (
<>
<TTSInitializer
apiKey={process.env.NEXT_PUBLIC_TTS_API_KEY}
mode="bundled" // Skip CDN attempt in bundled apps
/>
<YourContent />
</>
);
}Configuration Options
Global Init (All defaults built-in)
window.SupernalTTS.init({
// Required (defaults to https://www.tts.supernal.ai if not provided)
apiKey: 'YOUR_API_KEY',
// Optional overrides (these all have smart defaults)
apiUrl: 'https://www.tts.supernal.ai', // Custom API endpoint
provider: 'openai', // TTS provider
voice: 'alloy', // Default voice
speed: 1.0, // Playback speed (0.25-4.0)
clientSideSpeed: true, // Use browser speed (saves $$, recommended!)
showBranding: true, // Show "Powered by Supernal"
devMode: false // Enable debug logging
});Per-Widget Options
<!-- Override voice for specific widget -->
<div class="supernal-tts-widget"
data-text="Professional narration"
data-voice="nova"
data-provider="openai">
Professional narration
</div>
<!-- Advanced controls -->
<div class="supernal-tts-widget"
data-text="Article content"
data-voices="alloy,echo,fable" <!-- Multiple voice options -->
data-enable-speed="true" <!-- Show speed slider -->
data-enable-progress="true"> <!-- Show progress bar -->
Article content
</div>Advanced: Programmatic Control
Manual Widget Addition
const tts = window.SupernalTTS.getInstance();
// Add TTS to any element
tts.addWidget(
document.querySelector('#my-element'),
'Text to speak',
{ voice: 'alloy', provider: 'openai' }
);Zero-Config Auto-Init
For truly zero-config, use data attributes:
<script src="https://unpkg.com/@supernal/tts-widget@1/dist/widget.js"
data-supernal-tts-auto-init='{"apiKey":"YOUR_KEY"}'></script>The widget reads the config and initializes automatically!
Version Pinning
<!-- Auto-update within v1.x.x (recommended) -->
<script src="https://unpkg.com/@supernal/tts-widget@1/dist/widget.js"></script>
<!-- Lock to specific version -->
<script src="https://unpkg.com/@supernal/[email protected]/dist/widget.js"></script>
<!-- Always latest (not recommended for production) -->
<script src="https://unpkg.com/@supernal/tts-widget@latest/dist/widget.js"></script>Automatic API Key Detection
The widget automatically detects API keys from common framework environment variables, eliminating boilerplate:
// ✅ Minimal configuration - API key auto-detected
SupernalTTS.init({
apiUrl: window.location.origin
// provider defaults to 'openai'
// apiKey auto-detected from NEXT_PUBLIC_TTS_API_KEY (or similar)
});Supported environment variables (checked in order):
NEXT_PUBLIC_TTS_API_KEY- Next.jsVITE_TTS_API_KEY- VitePUBLIC_TTS_API_KEY- SvelteKit, AstroREACT_APP_TTS_API_KEY- Create React App
How it works:
- Widget checks for these variables at runtime (bundlers expose them client-side)
- If found, automatically uses the key for API authentication
- You can still override by explicitly passing
apiKeyoption - Only works in browser (server-side rendering won't auto-detect)
Example - Next.js:
# .env.local
NEXT_PUBLIC_TTS_API_KEY=your-api-key-here// No need to pass apiKey - auto-detected!
SupernalTTS.init({
apiUrl: 'https://tts.supernal.ai'
});Client-Side Speed Adjustment
By default (clientSideSpeed: true), the widget uses the browser's native preservesPitch feature with playbackRate to adjust speed instantly without regenerating audio. This:
- ✅ Saves significant costs - no need to generate audio at multiple speeds
- ✅ Instant speed changes - no loading/regeneration delay
- ✅ Maintains pitch quality - uses browser's time-stretching algorithm
- ✅ Works with already-cached audio
Set clientSideSpeed: false if you need server-side speed generation with provider-specific pitch correction (more expensive but may have slightly different quality characteristics for extreme speed changes).
Widget Data Attributes
<!-- Custom voice per widget -->
<div class="tts-widget"
data-text="Professional voice example"
data-voice="coral"
data-provider="openai">
Professional voice example
</div>
<!-- Custom speed -->
<div class="tts-widget"
data-text="Fast speech"
data-speed="1.5">
Fast speech
</div>Available Voices
OpenAI
alloy,echo,fable,onyx,nova,shimmer,coral,sage,verse
Mock (Testing)
mock-voice-1,mock-voice-2,mock-voice-3
When to Use What?
| Scenario | Recommended Approach | Why |
|----------|---------------------|-----|
| New projects | import { WidgetLoader } from '.../ loader' | Auto-updates, has fallback |
| Existing code | import { SupernalTTS } from '...' | No changes needed |
| Corporate firewall | import { SupernalTTS } from '...' | Works offline/air-gapped |
| Static HTML blog | Direct CDN @1 | Simple, no build step |
| Need predictability | import { SupernalTTS } from '...' | Manual upgrades only |
| CSP restrictions | import { SupernalTTS } from '...' | No external scripts |
Migration from v1.2.x
See MIGRATION.md for detailed upgrade guide.
TL;DR: No breaking changes. Existing code works as-is. New smart loader is opt-in.
// Old (v1.2.x - still works exactly the same)
import { SupernalTTS } from '@supernal/tts-widget';
SupernalTTS.init({ ... });
// New (v1.3.0 - opt-in for auto-updates)
import { WidgetLoader } from '@supernal/tts-widget/loader';
const widget = await WidgetLoader.load();
widget.init({ ... });No action required for existing code! The default export is unchanged.
Documentation
Full documentation: https://tts.supernal.ai
Development
This is the SOURCE OF TRUTH for the Supernal TTS widget. All changes should be made here.
Directory Structure
packages/@supernal/tts-widget/
├── src/
│ ├── widget.ts # TypeScript source (EDIT THIS)
│ └── widget.css # Styles
├── dist/ # Built output (DO NOT EDIT)
│ ├── widget.js # Bundled JavaScript
│ ├── widget.css # Copied styles
│ └── widget.d.ts # TypeScript declarations
├── package.json
└── tsconfig.jsonBuild Process
Build the widget:
npm run buildThis runs:
tsc- Compiles TypeScript to JavaScriptesbuild- Bundles and minifies to a single IIFE filecp- Copies CSS to dist
From monorepo root:
npm run build:widgetDocs site automatically copies widget on build/start:
cd docs-site npm run copy-widget # or `npm start` / `npm run build`
Features
- TypeScript: Proper typing and IDE support
- Dev Mode Cache Clear: Red button in dev mode to clear local cache
- Absolute URLs: Handles both relative and absolute audio URLs
- Branding: Optional Supernal Intelligence badge
- Responsive: Mobile-friendly design
- Dark Mode: Automatic dark mode support
Usage in Docs Site
The docs-site imports the widget from this package via:
- Build script:
copy-widgetindocs-site/package.jsoncopies built files tostatic/ - Component:
TTSWidgetindocs-site/src/components/TTSWidget/loads from/js/widget.js
DO NOT edit files in docs-site/static/js/ or docs-site/static/css/ directly!
Making Changes
- Edit
src/widget.tsorsrc/widget.css - Run
npm run build(ornpm run devfor watch mode) - Test in docs-site:
cd ../../../docs-site && npm start - The widget will be automatically copied and loaded
Publishing
When ready to publish to npm:
npm version patch|minor|major
npm publish