PS
All projects
Completed2026

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.

FlutterDartGroq APIWhisper

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.

  1. groq_service.dart

    The only networking code: builds the multipart request, calls Whisper, parses the response.

    httphttp_parser
  2. api_key_store.dart

    Reads and writes the user's key to device storage, isolating persistence from UI.

    shared_preferences
  3. audio_list_screen.dart

    Selection and listing of audio items.

    Flutterfile_picker
  4. transcription_screen.dart

    Displays returned transcripts.

    Flutter
  5. audio_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.

  1. Key is read from device storage

    api_key_store loads the user's saved Groq key, prompting once if absent.

  2. User selects an audio file

    file_picker returns a platform file handle.

  3. Multipart request is built

    groq_service assembles the file with correct MIME typing via http_parser.

  4. Whisper endpoint is called directly

    The request goes device-to-Groq, authorised with the user's own key.

  5. 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.