Feature spec
IntermediateCategory
ai-features
Build with AI
4–8 hours with Lovable or V0
Custom build
1–2 weeks custom dev
Running cost
$0–5/mo up to 100 users; $25–40/mo at 1K users
Works on
Everything it takes to ship Voice Activated Content Search — parts, prompts, and real costs.
Voice-activated content search combines three layers: a microphone input (Web Speech API or Whisper), a query normalizer that strips filler words, and a pgvector semantic search engine that matches meaning rather than exact keywords. With Lovable or V0 you can ship a working version in 4–8 hours. Running costs are minimal — OpenAI embedding queries cost roughly $0.0002 per search, and the Web Speech API is free for most users.
What Voice Activated Content Search Actually Is
Voice-activated content search is the combination of two features that are individually straightforward but powerful together: voice input (the user speaks a query) and semantic search (the app finds relevant content by meaning, not just keyword match). A user speaks 'show me articles about getting funding for a startup' and the app returns results about venture capital, angel investors, and pitch decks — even if none of those exact words appear in the query. The microphone layer uses the Web Speech API (free, runs in Chrome/Safari) with an optional Whisper API fallback for Firefox. The semantic layer uses pgvector in Supabase with OpenAI text-embedding-3-small to convert both query and content into vector embeddings and rank by cosine similarity. A query normalizer between the two strips filler words like 'um', 'search for', and 'show me' before the embedding call.
What users consider table stakes in 2026
- Microphone button prominent in the search bar or as a floating action button — never hidden in a menu
- Clear visual feedback while listening: pulsing ring animation or animated waveform, not just a static mic icon
- Transcript shown in the search input field as the user speaks (interim results in greyed text, confirmed text in black) so they can edit before the search fires
- Semantic search results that surface relevant content even when the spoken query and article text share no exact words
- Voice result announced via text-to-speech reading the first result title aloud after results appear
- Recent voice searches saved per user so they can repeat a query without speaking again
- Graceful fallback to typed search when microphone permission is denied, with a clear tooltip explaining why
Anatomy of the Feature
Seven components. The mic input and search UI are straightforward. The embedding pipeline and ivfflat index are where first builds break — wrong model pairing and missing index creation are the two most common failures.
Voice Input Button
UIA floating action button or mic icon inside the search bar. On click it triggers the SpeechRecognition API (web) or the speech_to_text Flutter package (mobile). Shows an animated pulsing ring during the active listening state. Pressing again stops listening early.
Note: Requires HTTPS and a real browser origin — microphone access is blocked in sandboxed preview iframes in both Lovable and V0. Test voice on the published URL.
Speech-to-Text Layer
ServiceWeb Speech API SpeechRecognition with interimResults: true shows live transcript as the user speaks. When the final result fires (event.results[i].isFinal === true), the transcript is sent to the normalizer. A Supabase Edge Function calling OpenAI Whisper API (/v1/audio/transcriptions) serves as fallback for Firefox, where SpeechRecognition is not implemented.
Note: The Web Speech API is free and covers Chrome desktop, Android Chrome, and Safari iOS. Whisper API costs $0.006/minute of audio — at 5 seconds per average voice query, that's $0.0005 per Firefox user search.
Query Normalizer
BackendA Supabase Edge Function that receives the raw transcript and strips filler words ('um', 'uh', 'search for', 'show me', 'find me', 'I want'), lowercases the string, trims leading and trailing punctuation, and returns the cleaned query string. This step dramatically improves embedding quality because embedding models treat 'show me articles about funding' differently from 'articles funding'.
Note: Implement the filler word list as an array of regex patterns. Keep the normalizer fast — it runs in the critical path between speech recognition and the embedding call, and any latency is felt as lag before results appear.
Semantic Search Engine
BackendA Supabase Edge Function that (1) embeds the normalized query using OpenAI text-embedding-3-small, (2) calls a Supabase RPC that runs a pgvector similarity search on the content_embeddings table using the <=> (cosine distance) operator, and (3) filters to content_type if the user's search is scoped. Returns top-10 results with similarity scores.
Note: content_embeddings must have been populated in advance by a separate indexing process that embeds all existing content items using the same model (text-embedding-3-small, 1536 dimensions). Mixing models produces incomparable vectors and zero relevant results.
Results Ranker
BackendHybrid scoring combines the pgvector cosine similarity score with Supabase's full-text search ts_rank on the same content rows. The final score is a weighted sum (e.g. 0.7 * vector_score + 0.3 * text_rank). This hybrid approach handles both the semantic meaning of spoken queries and exact keyword matches like proper nouns or product names.
Note: Start with pgvector-only (simpler) and add the ts_rank hybrid layer if early user testing shows relevant content being missed.
Voice Result Announcer
UIAfter results render, the Web Speech API SpeechSynthesis (web) or flutter_tts (mobile) reads the first result title and a brief snippet aloud. A toggle in user settings lets users disable this if they find it annoying. The announcer must be cancelled before a new voice search starts to prevent the TTS output from being picked up by the microphone as a new query.
Note: Cancel any active SpeechSynthesis utterance at the start of every new SpeechRecognition session. On iOS Safari, TTS requires a user gesture to start — it can't autoplay on page load.
Search History
DataA Supabase voice_searches table logs each voice query: the raw transcript, normalized query, and result count. Displayed in a 'Recent searches' dropdown below the search bar. RLS restricts each user to their own history. Analytics over this table surface which queries return zero results, indicating content gaps.
The data model
Two tables: content_embeddings (pre-indexed vector representations of your content) and voice_searches (per-user search history). The pgvector extension and ivfflat index are required for fast similarity search. Run this in the Supabase SQL Editor — paste and execute the entire block.
1-- Enable pgvector extension (run once per Supabase project)2create extension if not exists vector;34-- Content embeddings table: pre-indexed at content creation/update time5create table public.content_embeddings (6 id uuid primary key default gen_random_uuid(),7 content_id uuid not null,8 content_type text not null, -- 'article', 'product', 'video', etc.9 embedding vector(1536) not null, -- text-embedding-3-small dimension10 updated_at timestamptz not null default now()11);1213-- ivfflat index for fast cosine similarity search14-- lists = 100 is appropriate for up to ~1M rows15create index content_embeddings_vector_idx16 on public.content_embeddings17 using ivfflat (embedding vector_cosine_ops)18 with (lists = 100);1920create index content_embeddings_type_idx21 on public.content_embeddings (content_type);2223-- Voice search history: per-user, RLS-protected24create table public.voice_searches (25 id uuid primary key default gen_random_uuid(),26 user_id uuid references auth.users(id) on delete cascade not null,27 raw_transcript text not null,28 normalized_query text not null,29 result_count int not null default 0,30 created_at timestamptz not null default now()31);3233alter table public.content_embeddings enable row level security;34alter table public.voice_searches enable row level security;3536create policy "Authenticated users can read embeddings"37 on public.content_embeddings for select38 to authenticated39 using (true);4041create policy "Users can view own voice searches"42 on public.voice_searches for select43 using (auth.uid() = user_id);4445create policy "Users can insert own voice searches"46 on public.voice_searches for insert47 with check (auth.uid() = user_id);4849-- Index for recent searches dropdown (newest first per user)50create index voice_searches_user_recent_idx51 on public.voice_searches (user_id, created_at desc);Heads up: The ivfflat index is the most important step — without it, pgvector performs a full sequential scan that becomes unusably slow past ~10K rows. The lists=100 parameter is suitable for content libraries up to about 1 million items; increase proportionally for larger datasets. Run the SQL in the Supabase Dashboard → SQL Editor.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
Full pipeline: continuous speech recognition with Whisper for high accuracy, query normalization, hybrid pgvector and BM25 search, TTS result announcement, content re-indexing webhook triggered on new content. Right choice when search is a core product differentiator.
Step by step
- 1Web Speech API for Chrome/Safari + Whisper API for Firefox with automatic browser detection and seamless fallback
- 2Query normalization with configurable filler word list and language-specific rules
- 3Hybrid search: pgvector cosine similarity weighted with Supabase ts_rank full-text scoring for best combined relevance
- 4Content re-indexing webhook: when a new article or product is published, a Supabase database trigger fires an Edge Function that embeds the new item and inserts into content_embeddings
- 5Embedding cache: popular query embeddings cached in Redis/Upstash to avoid repeated OpenAI API calls for identical searches
Where this path bites
- Embedding pre-indexing for large content libraries (10K+ items) requires a batch job and meaningful Supabase compute — allow for this in timeline planning
- True multi-language semantic search (query in English, results in French) requires cross-lingual embedding models beyond text-embedding-3-small
Third-party services you'll need
The mic and speech recognition are free for most users via the Web Speech API. Costs come in at the embedding layer — fortunately, OpenAI's text-embedding-3-small is one of the cheapest APIs available.
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| OpenAI Embeddings API (text-embedding-3-small) | Converts voice query text into a 1536-dimension vector for pgvector similarity search; also used to pre-index content library | No free tier; pay-as-you-go | $0.02 per 1M tokens; indexing 10K articles costs ~$0.10 one-time; queries ~$0.0002 per search |
| OpenAI Whisper API | Speech-to-text fallback for Firefox and high-accuracy transcription; called via Supabase Edge Function | No free tier; pay-as-you-go | $0.006 per minute of audio (approx); a 5-second voice query = $0.0005 |
| Supabase pgvector | Vector similarity search for semantic content retrieval; included in all Supabase plans | Available on Free plan; limited compute | Pro $25/mo includes sufficient compute for vector search up to ~1M rows |
| Algolia (alternative to pgvector) | Managed search with NLP query understanding; simpler setup than pgvector but less control over embeddings | 10K records, 10K search operations/mo | Growth $0.50 per 1K search operations (approx) |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
OpenAI embeddings for 100 users × 20 searches/mo = 2,000 queries at ~$0.0002 each = $0.40 in embedding costs. Supabase free tier covers the database. Whisper fallback for Firefox users adds pennies. Round to ~$3/mo including any Supabase Edge Function invocations.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
pgvector returns zero results — embedding model mismatch
Symptom: If content was pre-indexed using OpenAI's ada-002 model (1536 dims, legacy) but voice queries are embedded using text-embedding-3-small (also 1536 dims), the vectors occupy completely different regions of the embedding space. Cosine similarity between vectors from different models is meaningless — queries return empty results even when the content clearly matches the spoken query.
Fix: Use the exact same model for both content indexing and query embedding. Pick text-embedding-3-small and use it everywhere. If you need to switch models later, re-index all existing content with the new model before deploying the change.
Voice search fires before the user finishes speaking
Symptom: The SpeechRecognition API fires onresult events for both interim (isFinal: false) and final (isFinal: true) results. If the search pipeline is triggered on any onresult event rather than only the final one, users see search results flash and update mid-sentence as interim transcripts change — disorienting and wasteful of API calls.
Fix: In the onresult handler, check event.results[event.results.length - 1].isFinal === true before passing the transcript to the normalizer and search pipeline. Display interim results in the search input field (greyed text) for feedback, but only submit on the final result.
ivfflat index missing — queries hang or time out on large content sets
Symptom: Without the ivfflat index, pgvector performs a sequential scan over every row in content_embeddings to compute cosine distance. Up to about 5,000 rows this is fast enough to go unnoticed. Above that, query time grows linearly with row count and becomes unusably slow. AI tools commonly create the table but miss the separate CREATE INDEX statement.
Fix: After build, verify the index exists in the Supabase Dashboard → Database → Indexes. If missing, run: CREATE INDEX ON content_embeddings USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100). This takes a few seconds on small datasets and a few minutes on large ones — run it during off-peak hours on production.
Semantic search returns irrelevant content across types
Symptom: When content_embeddings stores articles, products, and videos all in the same table without filtering, a voice query for 'how to set up Stripe' in an articles-only context may surface product listings or video thumbnails with high cosine similarity. The embedding space is shared across all content, so type boundaries must be enforced explicitly in the query.
Fix: Pass a content_type parameter from the frontend to the search Edge Function and add a WHERE content_type = $2 clause to the pgvector query alongside the cosine distance filter. Expose a content type selector in the UI if users should be able to scope their voice searches.
TTS result announcement plays over the next voice query
Symptom: When the voice result announcer reads the first search result aloud, SpeechSynthesis.speak() and SpeechRecognition.start() can be active simultaneously. The microphone picks up the TTS output as a new voice query, creating an infinite loop where the app searches for what it just said.
Fix: Call speechSynthesis.cancel() at the very start of the mic button click handler, before calling SpeechRecognition.start(). Also add a 300ms delay between TTS finishing (via the onend event) and re-enabling the mic button to prevent accidental immediate re-triggering.
Best practices
Use the exact same OpenAI embedding model (text-embedding-3-small, 1536 dims) for both content indexing and query embedding — mixing models silently produces irrelevant results
Create the ivfflat index before going live, not after noticing slowness — a missing index on a 50K-row content library makes every voice search time out
Trigger content re-indexing automatically when new content is published via a Supabase database trigger rather than relying on manual batch jobs
Show the transcript in the search input as soon as it's confirmed so users can edit before results load — this one UX detail dramatically reduces 'it didn't understand me' complaints
Set a cosine similarity threshold (cosine distance < 0.25 is a reasonable starting point) and return an explicit 'no results found' state rather than surfacing low-relevance content
Cache embeddings for the 100 most common voice queries in a simple Supabase table — at scale these dominate the embedding API spend and are easily cached
Cancel any active SpeechSynthesis utterance at the start of each new recognition session to prevent TTS output from being picked up as a new search query
Track which voice queries return zero results in the voice_searches table — these reveal content gaps in your library more directly than any analytics tool
When You Need Custom Development
AI tools handle the core voice-to-semantic-search pipeline well. A few scenarios require a dedicated engineering effort:
- Content library has 100K+ items requiring partitioned vector indexes, dedicated pgvector compute, and potentially a migration to a specialized vector database like Pinecone or Qdrant
- Multi-language semantic search where a user can speak in English and retrieve results written in Spanish, French, or Japanese — cross-lingual embedding models beyond text-embedding-3-small are required
- Real-time index updates as new content is published at high velocity — a streaming embedding pipeline replaces the batch indexing job
- Privacy requirement that all speech processing must stay on-device with no audio sent to Whisper or any cloud service — on-device models like Whisper.cpp via WASM are complex to ship
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
What's the difference between voice search and voice-activated content search?
Standard voice search is keyword-based: the spoken transcript is matched against text fields in the database using exact or fuzzy string matching. Voice-activated content search adds a semantic layer — the spoken query and all content items are converted into vector embeddings, and results are ranked by meaning similarity using pgvector. This means 'how do I raise money' returns results about venture capital even if the articles never use that phrase.
How do I set up pgvector in Supabase?
Run CREATE EXTENSION IF NOT EXISTS vector in the Supabase SQL Editor. pgvector is pre-installed on all Supabase Postgres instances — you just need to enable it. Then create your embeddings table with a vector(1536) column and add the ivfflat index. The full SQL is in the data model section above.
How do I index my existing content for vector search?
Write a one-time script (a Supabase Edge Function works well) that reads all your existing content items in batches, sends each item's text to OpenAI text-embedding-3-small, and inserts the returned 1536-dimension vector into the content_embeddings table alongside the content_id. For 10,000 articles this takes a few minutes and costs about $0.10 in API fees. After that, add automatic re-indexing when new content is published.
What is the OpenAI embeddings cost for voice search queries?
text-embedding-3-small costs $0.02 per 1 million tokens. An average voice search query is about 10 words = roughly 10 tokens. That means 100,000 voice searches cost $0.02 in embedding fees. Even at high scale, embedding query costs are rarely the dominant line item — Supabase compute and Whisper fallback costs are typically larger.
Can I add voice search on top of an existing text search feature?
Yes — and it's the recommended approach. Keep your existing text search as the default. Add the microphone button as a parallel input method that captures a transcript and feeds it into the same search pipeline. The semantic search Engine can sit alongside your existing keyword search and you can A/B test which delivers better results for your content.
How do I handle users who speak in multiple languages?
The Web Speech API's lang property accepts a BCP 47 language tag (e.g. 'es-ES', 'fr-FR'). Set it to match the user's active language setting in your app. For Whisper, it auto-detects the language from the audio. For semantic search, text-embedding-3-small handles multilingual input reasonably well for major languages, but cross-language retrieval (speaking in one language, finding content in another) requires specialized cross-lingual embedding models.
How do I make voice search results more accurate?
Three levers: (1) improve content indexing quality — embed rich content including headings, body text, and metadata rather than just titles; (2) tune the cosine similarity threshold — lower the distance threshold (e.g. from 0.3 to 0.2) to require stronger matches; (3) add the hybrid ranking layer that weights pgvector similarity with Supabase full-text ts_rank for queries containing exact product names or proper nouns.
Does voice-activated search work offline?
Partially. The Web Speech API requires an internet connection on Chrome because it sends audio to Google's servers for transcription. The speech_to_text Flutter package can use on-device recognition, which works offline on iOS and Android (using Apple and Google's on-device models). The pgvector semantic search always requires a Supabase connection — a true offline semantic search would need a local vector database, which is a significant custom engineering effort.
Need this feature production-ready?
RapidDev builds voice activated content search into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.