Every podcast needs a hook—a short, memorable intro that sets the tone, tells listeners what they’re about to hear, and builds brand consistency. Recording a fresh intro for each episode can be tedious, especially for solo creators juggling editing, publishing, and promotion. With modern voice AI and text‑to‑speech (TTS) technology, you can automate that process and generate a polished intro in seconds.
In this post I’ll walk you through building a Podcast Intro Generator that takes a few lines of script, injects your podcast name, and spits out a high‑quality audio file ready for your editing workflow. The core of the solution relies on ElevenLabs, a state‑of‑the‑art TTS service that offers realistic voice cloning and easy API access. Let’s dive in!
+-------------------+ +--------------------+ +-------------------+
| User Input | ---> | Script Builder | ---> | ElevenLabs TTS |
| (title, tagline) | | (template + data) | | API (audio file) |
+-------------------+ +--------------------+ +-------------------+
|
v
+-----------------+
| Post‑process |
| (fade‑in/out, |
| normalization)|
+-----------------+
|
v
+-----------------+
| Output File |
+-----------------+
ElevenLabs provides a free tier that’s generous enough for testing, and a paid plan for high‑volume production. The API uses a simple REST interface and returns audio in MP3 format by default.
Quick link: Sign up and grab your API key here → https://try.elevenlabs.io/kr07zfuqn1bp
Once you have the key, you can start sending requests. Below is a minimal Python wrapper that handles authentication, voice selection, and audio retrieval.
import requests
import json
from pathlib import Path
ELEVEN_API_KEY = "YOUR_ELEVENLABS_API_KEY"
BASE_URL = "https://api.elevenlabs.io/v1"
def generate_speech(text: str, voice_id: str = "default", output_path: Path = Path("intro.mp3")):
"""Send a TTS request to ElevenLabs and save the resulting audio."""
url = f"{BASE_URL}/text-to-speech/{voice_id}"
headers = {
"xi-api-key": ELEVEN_API_KEY,
"Content-Type": "application/json"
}
payload = {
"text": text,
"model_id": "eleven_monolingual_v1",
"voice_settings": {
"stability": 0.75,
"similarity_boost": 0.85
}
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
output_path.write_bytes(response.content)
print(f"✅ Intro saved to {output_path}")
if __name__ == "__main__":
script = "Welcome to TechTalk, the podcast where we decode the latest in software engineering. Today we explore serverless architectures."
generate_speech(script, voice_id="YOUR_CLONED_VOICE_ID")
Tip: If you don’t have a cloned voice yet, create one in the ElevenLabs dashboard. Upload a few seconds of your own voice, and the service will generate a voice_id you can reuse across episodes.
Hard‑coding the script defeats the purpose of a generator. Instead, use Python’s f‑strings or a small templating engine like Jinja2 to inject episode‑specific data.
from jinja2 import Template
INTRO_TEMPLATE = """
Welcome to {{ podcast_name }}, the show that {{ tagline }}.
In today's episode, "{{ episode_title }}", we'll dive into {{ episode_topic }}.
Stay tuned!
"""
def build_intro(podcast_name, tagline, episode_title, episode_topic):
tmpl = Template(INTRO_TEMPLATE.strip())
return tmpl.render(
podcast_name=podcast_name,
tagline=tagline,
episode_title=episode_title,
episode_topic=episode_topic
)
Now you can call build_intro() with data from a CSV, a content management system, or even a simple CLI prompt.
script = build_intro(
podcast_name="DevBytes",
tagline="break down complex dev topics into bite‑size episodes",
episode_title="Zero‑Trust Security",
episode_topic="how to implement zero‑trust in cloud-native apps"
)
print(script)
The output will be a ready‑to‑speak string that you feed into generate_speech() from the previous section.
Most podcasts prefer a 0.5‑second fade‑in and fade‑out to smooth the transition. The pydub library makes this trivial.
from pydub import AudioSegment
def add_fade(audio_path: Path, fade_ms: int = 500) -> Path:
audio = AudioSegment.from_file(audio_path)
faded = audio.fade_in(fade_ms).fade_out(fade_ms)
out_path = audio_path.with_name(f"{audio_path.stem}_faded{audio_path.suffix}")
faded.export(out_path, format="mp3")
return out_path
raw_path = Path("intro.mp3")
faded_path = add_fade(raw_path)
print(f"✅ Faded intro saved to {faded_path}")
Combine the steps into a single function for convenience:
def generate_podcast_intro(data: dict, voice_id: str, out_dir: Path = Path(".")):
script = build_intro(**data)
raw_file = out_dir / "intro_raw.mp3"
generate_speech(script, voice_id=voice_id, output_path=raw_file)
final_file = add_fade(raw_file)
return final_file
If you like the terminal, here’s a tiny click‑based CLI that ties everything together.
import click
from pathlib import Path
@click.command()
@click.option("--podcast", prompt="Podcast name", help="Your podcast's name")
@click.option("--tagline", prompt="Tagline", help="A short description of your show")
@click.option("--title", prompt="Episode title", help="Current episode's title")
@click.option("--topic", prompt="Episode topic", help="What you’ll discuss")
@click.option("--voice-id", prompt="ElevenLabs voice ID", help="Your cloned voice")
def main(podcast, tagline, title, topic, voice_id):
data = {
"podcast_name": podcast,
"tagline": tagline,
"episode_title": title,
"episode_topic": topic,
}
out_file = generate_podcast_intro(data, voice_id, out_dir=Path("outputs"))
click.echo(f"✅ Your intro is ready: {out_file}")
if __name__ == "__main__":
main()
Run it with:
$ python generate_intro.py
You’ll be prompted for the details, and within seconds a polished intro file lands in the outputs/ folder.
A subtle music bed can elevate the vibe. Grab a royalty‑free loop (e.g., from Free Music Archive) and mix it with the spoken intro using pydub’s overlay:
def add_music(intro_path: Path, music_path: Path, music_volume_db: int = -15) -> Path:
intro = AudioSegment.from_file(intro_path)
music = AudioSegment.from_file(music_path).apply_gain(music_volume_db)
if len(music) < len(intro):
repeats = (len(intro) // len(music)) + 1
music = music * repeats
mixed = intro.overlay(music[:len(intro)])
out_path = intro_path.with_name(f"{intro_path.stem}_with_music{intro_path.suffix}")
mixed.export(out_path, format="mp3")
return out_path
Now you can call add_music(faded_path, Path("assets/loop.mp3")) to produce a final, broadcast‑ready intro.
| Tip | Why it matters |
|---|---|
| Use a consistent voice | Listeners associate a particular timbre with your brand. Clone your own voice once and stick to that voice_id . |
| Keep scripts under 30 seconds | Longer TTS runs increase latency and can sound less natural. Short intros stay punchy. |
| Normalize loudness to -16 LUFS | Most podcast platforms target -16 LUFS; use pydub orffmpeg to batch‑normalize if you have many episodes. |
| Cache generated intros | If you re‑run the same script, store the MP3 locally to avoid unnecessary API calls (and save credits). |
You now have a complete, end‑to‑end pipeline:
All of this can be automated in a CI/CD job, a GitHub Action, or a simple cron script—so each new episode gets a fresh intro without you lifting a finger.
If you’re curious about how realistic voice cloning can streamline your podcast production, head over to ElevenLabs and grab your API key: https://try.elevenlabs.io/kr07zfuqn1bp.
Plug the key into the snippets above, and you’ll have a custom intro generator up and running in minutes. Happy coding, and may your podcasts always start with a bang!