AudioScript
Local-first audio transcription on your own API key
Flutter app that transcribes audio files through Groq's Whisper API, with the user's key held in local storage and never routed through a server of mine.
0
Backend servers
iOS · Android · Desktop · Web
Platforms
On-device
Key storage
Overview
AudioScript is a small, focused Flutter app: pick an audio file, get a transcript. It calls Groq's hosted Whisper endpoint directly from the device using an API key the user supplies.
It's deliberately narrow. There's no account, no sync, no server — which is precisely what makes it useful for anyone who doesn't want their recordings passing through someone else's infrastructure.
Problem
Transcription tools generally ask you to upload audio to a service you don't control, sign up for an account, and trust an opaque retention policy. For a lecture, an interview or a private voice memo, that's a real cost.
The underlying models are cheap and fast to access directly. The friction is entirely in the packaging — there's no reason a simple client can't talk to the API on the user's behalf, on the user's key.
Solution
A thin client with no backend of its own. The user provides a Groq API key once; it's persisted on-device via shared_preferences and used for every subsequent request.
Audio is selected with a native file picker and posted as multipart directly to Groq's Whisper endpoint. The transcript comes back to the device and stays there.
The result is an app where the trust question is simple to answer: the only party receiving the audio is the inference provider the user chose and pays.
Features
Bring your own key
The user's Groq API key is entered once and stored locally — no account, no server-side key custody.
Native file selection
file_picker handles audio selection across platforms with the platform's own picker.
Direct multipart upload
Audio is posted straight to the Whisper endpoint with correct content typing via http_parser.
Transcript view
A dedicated screen for reading the returned transcription.
Local audio list
Selected items are tracked in-app through a typed model rather than ad hoc state.
Cross-platform build targets
Configured for iOS, Android, macOS, Linux, Windows and web from one codebase.
Tech Stack
Client
- Flutter
- Dart
Files
- file_picker
Network
- http
- http_parser (multipart)
Storage
- shared_preferences
Inference
- Groq API (Whisper)
Architecture
Four concerns, one file each. The app is small enough that the architecture's job is to stay legible rather than to scale — the service layer is the only thing that knows about the network.
groq_service.dart
The only networking code: builds the multipart request, calls Whisper, parses the response.
httphttp_parserapi_key_store.dart
Reads and writes the user's key to device storage, isolating persistence from UI.
shared_preferencesaudio_list_screen.dart
Selection and listing of audio items.
Flutterfile_pickertranscription_screen.dart
Displays returned transcripts.
Flutteraudio_item.dart
Typed model for a selected file, keeping screen state structured.
Dart
Folder Structure
- lib/
- Whole application — five files and a model
- ├── main.dart
- App entry and routing
- ├── api_key_store.dart
- Local persistence of the user's API key
- ├── groq_service.dart
- Multipart upload to Groq Whisper
- ├── audio_item.dart
- Typed model for a selected audio file
- ├── audio_list_screen.dart
- File selection and list
- └── transcription_screen.dart
- Transcript display
API Flow
One round trip, no intermediary.
Key is read from device storage
api_key_store loads the user's saved Groq key, prompting once if absent.
User selects an audio file
file_picker returns a platform file handle.
Multipart request is built
groq_service assembles the file with correct MIME typing via http_parser.
Whisper endpoint is called directly
The request goes device-to-Groq, authorised with the user's own key.
Transcript is rendered locally
The response is parsed and displayed; nothing is uploaded anywhere else.
Challenges
Handling a user-supplied credential responsibly
Bring-your-own-key shifts custody to the device, which is the point — but it means the storage path matters. Isolating it in a single store rather than scattering reads through the UI keeps it to one thing to audit and one thing to upgrade to secure storage later.
Multipart uploads across platforms
Audio upload is fussier than JSON: content type has to be right or the endpoint rejects the file, and platform file handles differ. Explicit MIME typing through http_parser made this predictable rather than trial-and-error.
Resisting scope
Every obvious next feature — accounts, history sync, cloud storage — would have required the backend the app exists to avoid. Keeping it serverless was a product decision, not a technical shortcut.
Learnings
Not everything needs a backend. Removing the server removed the entire trust question along with it.
Bring-your-own-key is an underrated pattern for AI tooling — the user gets transparent pricing, the developer gets no key custody and no inference bill.
Small apps stay maintainable when each file has exactly one job, even at six files.
Getting multipart content types right is most of the work in any file-upload integration.
Future Improvements
Move the API key to platform secure storage rather than shared preferences.
In-app recording, so the tool doesn't depend on another app producing the file.
Export to text, Markdown and SRT with timestamps.
Progress reporting and chunked handling for long recordings.
Speaker diarisation where the provider supports it.
A local Whisper option for fully offline transcription.