Local Whisper Transcription Pipeline for Playtest VOD Triage - 2026
Your Discord #playtest channel has forty-seven clips from last week. Three say “game froze.” Twelve are silent menu tours. One streamer found a soft-lock you already fixed. Nobody wrote timestamps. You are supposed to ship a fest demo in eleven weeks.
Mid-2026 micro-studios run more async playtests than ever—Discord, Steam Playtest, private keys—but feedback stays trapped in video. Uploading every VOD to a cloud transcription SaaS raises retention, cost, and NDA questions. Doing nothing means bugs hide until refund dashboard rows light up.
This AI Integration / Workflow post documents a local-first Whisper pipeline: extract audio from playtest VODs, transcribe offline, run summary templates, tag surface (Playtest vs fest_public per playtest isolation), and file playtest_vod_triage_receipt_v1.json beside BUILD_RECEIPT.
Non-repetition note: Local voice notes to quest drafts is narrative authoring; ElevenLabs + Ollama architecture is in-game NPC voice. This URL owns playtest VOD triage—not Steam metadata checklists, not shorts caption tools (Whisper mention in clips listicle is ancillary).
Why this matters now (May–October 2026)
- Discord clip volume — Async playtests scale faster than human note-taking.
- Steam Playtest invites — More surfaces → more “which build?” confusion without transcripts tied to
build_label. - Privacy defaults — Players and partners expect VODs stay local unless consented.
- Fest crunch — Teams that skip triage in July drown in October duplicate reports.
- AI without cloud — Local Whisper is mature on laptop GPUs; use it for ops, not only captions.
Direct answer: Folder playtest-vod/inbox/, nightly Whisper pass, one summary.md per clip with build_id + surface, triage board in spreadsheet or GitHub Issues—receipt JSON proves the loop ran.
Who this is for
| Audience | Outcome |
|---|---|
| Solo dev | Turn clips into searchable text in one evening |
| Producer | Weekly triage ritual before standup |
| QA contractor | Standard handoff format |
| Engineers | Bug rows with timestamps + reproduction hints |
Time: ~90 minutes first pipeline setup; ~15 minutes per batch of 5 clips after.
Prerequisites: ffmpeg installed; Python 3.10+; optional NVIDIA GPU for faster Whisper.
Pipeline overview
Discord / OBS VOD (.mp4)
│
v
ffmpeg → audio.wav (16 kHz mono)
│
v
Whisper (local) → transcript.txt
│
v
Summary template → triage_row.md
│
v
Triage board (Issues / sheet)
│
v
playtest_vod_triage_receipt_v1.json
Rule: Raw VODs never leave playtest-vod/ without explicit consent policy documented in your playtest README.
Gates T1–T6 (batch pass)
| Gate | Pass criterion |
|---|---|
| T1 — Ingest | Filename includes build_id, date, surface |
| T2 — Audio | ffmpeg extract succeeds; duration logged |
| T3 — Transcript | Whisper output saved beside source |
| T4 — Summary | Template filled: repro, severity, area |
| T5 — Surface | playtest vs fest_public tag matches isolation map |
| T6 — Receipt | Batch row in playtest_vod_triage_receipt_v1.json |
Fail T5 → do not file fest_public fix until scope review.
Folder layout
playtest-vod/
README.md # consent + retention days
inbox/ # drop new mp4
audio/ # extracted wav
transcripts/ # whisper .txt + .json
summaries/ # triage_row.md per clip
archive/ # processed mp4 after T6
receipts/
playtest_vod_triage_receipt_v1.json
Retention opinion: Delete raw VODs after 30 days if NDA requires; keep transcript + summary longer for regression compare.
Step 1 — Ingest naming (beginner-critical)
YYYY-MM-DD_build-2026-05-24-playtest-03_surface-playtest_tester-initials.mp4
| Field | Example | Why |
|---|---|---|
build_id |
2026-05-24-playtest-03 |
Links to BUILD_RECEIPT |
surface |
playtest | fest_public |
Isolation discipline |
tester |
initials or anon-042 |
Privacy |
Beginner mistake: clip1.mp4 — unusable after a week.
Step 2 — Extract audio with ffmpeg
ffmpeg -i "inbox/clip.mp4" -vn -acodec pcm_s16le -ar 16000 -ac 1 "audio/clip.wav"
- 16 kHz mono is Whisper-friendly and smaller on disk.
- Log
duration_secin summary for “freeze at 4:12” searches.
Working dev: Batch script loops inbox/*.mp4 and skips if transcripts/clip.txt exists.
Step 3 — Local Whisper (not cloud)
Official project: openai/whisper (open source). Run locally:
pip install -U openai-whisper
whisper audio/clip.wav --model small --language en --output_dir transcripts/
Model pick (2026 laptop reality)
| Model | Speed | Accuracy | Use when |
|---|---|---|---|
tiny |
Fastest | Lowest | Smoke test pipeline |
base |
Fast | OK | Daily batches, clear speech |
small |
Medium | Good | Default for playtest |
medium |
Slow | Better | Noisy comms, accent-heavy |
large |
Slowest | Best | One-off critical bug clip |
Opinion: small on GPU or base on CPU beats uploading VODs to a SaaS for indie scale.
GPU vs CPU
| Hardware | Expectation |
|---|---|
| NVIDIA laptop | small practical overnight batch |
| Apple Silicon | small with Metal builds varies—test once |
| CPU-only | Use base; queue overnight |
Do not block fest work waiting for large on forty clips—transcript good enough to triage beats perfect prose.
Step 4 — Summary template (summaries/clip.md)
# Playtest VOD summary
build_id: 2026-05-24-playtest-03
surface: playtest
source_file: inbox/2026-05-24_....mp4
duration_sec: 612
transcribed_at: 2026-05-24T20:15:00Z
whisper_model: small
## Player-reported issue (one line)
Soft-lock after boss door when backtracking.
## Timestamp highlights
- 04:12 — "uh it's frozen here"
- 04:30 — menu still opens
- 05:01 — gives up, alt-tabs
## Repro guess
Backtrack from boss room to hub without clearing flag X.
## Severity
P1 — blocks session
## Area tags
save, progression, hub
## Action
- [ ] Engineer confirms flag
- [ ] Add to Wednesday smoke golden path?
Human step: Editor spends 3–5 minutes per summary fixing game terms Whisper misheard (“roguelike” → “road you like”). AI summarizes; humans authorize bug rows.
Optional local LLM summarize pass
After Whisper, you may run local Ollama on transcript.txt only—never upload video:
Prompt: Given this playtest transcript, fill the summary template sections
Repro guess, Severity, Area tags. Quote timestamps when present.
Pair with Ollama architecture HTTP patterns—not ollama run blocking stdout.
Privacy rule: If using cloud LLM at all, send text chunks only under written playtest consent—not raw VOD.
playtest_vod_triage_receipt_v1.json
{
"schema": "playtest_vod_triage_receipt_v1",
"batch_date": "2026-05-24",
"clips_processed": 5,
"whisper_model": "small",
"gates": {
"T1_ingest": true,
"T2_audio": true,
"T3_transcript": true,
"T4_summary": true,
"T5_surface": true,
"T6_receipt": true
},
"clips": [
{
"file": "2026-05-24_build-playtest-03_surface-playtest_aa.mp4",
"build_id": "2026-05-24-playtest-03",
"surface": "playtest",
"severity": "P1",
"issue_id": "GH-142"
}
],
"cloud_upload": false
}
Attach to release evidence playtest/ folder.
Surface tagging (mandatory)
From playtest isolation playbook:
surface value |
Fix list |
|---|---|
playtest |
Playtest branch backlog |
fest_public |
Fest promotion / public demo |
internal |
QA-only |
Industry failure: Playtest VOD mentions bug on wrong depot → team “fixes” fest_public while Playtest still broken. Transcript + build_id prevents this if T5 is enforced.
Triage board columns
| Column | Source |
|---|---|
issue_id |
GitHub / Linear |
build_id |
Filename + BUILD_RECEIPT |
surface |
T5 |
severity |
P0–P3 |
timestamp |
Summary |
status |
open / fixed / wontfix |
smoke_test |
Link Wednesday S-pass |
Pair with 18 playtest tools for forms and surveys—Whisper handles unstructured video.
Beginner path — one evening
- 20 min — Create folder layout + README consent blurb.
- 15 min — Install ffmpeg +
pip install openai-whisper. - 20 min — Process one clip end-to-end.
- 20 min — Fill summary template; open one GitHub issue.
- 15 min — Write receipt JSON for batch of one.
Success: You can search “frozen” across transcripts/ without rewatching forty minutes of video.
Working dev path — batch script sketch
#!/usr/bin/env python3
"""Batch: mp4 in inbox -> wav -> whisper -> stub summary path."""
import json, subprocess, pathlib
from datetime import datetime
INBOX = pathlib.Path("playtest-vod/inbox")
AUDIO = pathlib.Path("playtest-vod/audio")
TRANS = pathlib.Path("playtest-vod/transcripts")
MODEL = "small"
def extract_wav(mp4: pathlib.Path) -> pathlib.Path:
wav = AUDIO / (mp4.stem + ".wav")
if wav.exists():
return wav
subprocess.run([
"ffmpeg", "-y", "-i", str(mp4), "-vn",
"-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1", str(wav)
], check=True)
return wav
def transcribe(wav: pathlib.Path) -> None:
subprocess.run([
"whisper", str(wav), "--model", MODEL,
"--language", "en", "--output_dir", str(TRANS)
], check=True)
def main():
processed = []
for mp4 in sorted(INBOX.glob("*.mp4")):
wav = extract_wav(mp4)
transcribe(wav)
processed.append(mp4.name)
receipt = {
"schema": "playtest_vod_triage_receipt_v1",
"batch_date": datetime.utcnow().date().isoformat(),
"clips_processed": len(processed),
"whisper_model": MODEL,
"clips": [{"file": n} for n in processed],
"cloud_upload": False,
}
out = pathlib.Path("playtest-vod/receipts/playtest_vod_triage_receipt_v1.json")
out.write_text(json.dumps(receipt, indent=2), encoding="utf-8")
print("OK", len(processed))
if __name__ == "__main__":
main()
Extend with summary generation and issue API calls when ready—do not overbuild before T3 works once.
Weekly ritual (producer calendar)
| Day | Action |
|---|---|
| Monday | Collect clips to inbox/ |
| Tuesday night | Batch Whisper (laptop plugged in) |
| Wednesday | Human edit summaries; prioritize P0/P1 before demo smoke |
| Thursday | Link fixes to BUILD_RECEIPT rows; add Thursday receipt-row diff after Wednesday smoke (Process ritual on backlog—pair when published) |
| Friday | Archive processed VODs per retention |
playtest-vod/README.md consent blurb (copy)
# Playtest VOD handling
- Clips are processed **locally** with Whisper; full video is not uploaded to third-party AI by default.
- Retention: raw MP4/WAV deleted after 30 days; transcripts/summaries kept 12 months.
- By submitting a clip you confirm you have rights to share audio for bug triage.
- Streamers: label clips `surface` and `build_id` in filename.
- To opt into cloud text summarization: contact producer in writing.
Beginner: Paste into Discord pin when opening a playtest channel.
Severity rubric (P0–P3)
| Level | Definition | SLA (micro-studio norm) |
|---|---|---|
| P0 | Crash, data loss, soft-lock | Before next build upload |
| P1 | Major progression block | This sprint |
| P2 | UX confusion, balance | Backlog |
| P3 | Polish, typo | Fest-or-later |
Whisper summaries should propose severity; producer confirms in triage standup.
Cloud transcription vs local Whisper (2026)
| Factor | Cloud SaaS VOD upload | Local Whisper |
|---|---|---|
| Privacy | Policy-dependent | Disk stays yours |
| Cost | Per-minute at scale | Electricity + GPU time |
| Setup | Low | Medium (ffmpeg + Python) |
| Accuracy | Often strong | small+ is enough for triage |
| Batch forty clips | Bill spikes | Overnight script |
| NDA playtests | Legal review | Default safer |
Industry pattern: Use cloud only when legal and budget explicitly allow; default local for Discord dumps.
GitHub issue body template
## Playtest VOD triage
**Build:** 2026-05-24-playtest-03
**Surface:** playtest
**Source clip:** playtest-vod/inbox/....mp4
**Transcript:** playtest-vod/transcripts/....txt
### Summary
Soft-lock after boss door when backtracking.
### Timestamps
- 04:12 — frozen report
### Repro steps (guess)
1. Beat boss
2. Backtrack to hub
3. Interact with door
### Labels
bug, playtest, P1, progression
Links issues to code without pasting entire transcript into Discord.
Scaling before fest volume (July–September)
| Clips/week | Suggested ops |
|---|---|
| 1–5 | Manual summary edit |
| 6–15 | Nightly Whisper batch |
| 16–40 | Split by surface; two reviewers |
| 40+ | Moratorium new playtest keys until P0 clear |
Pair with playtest isolation—volume on wrong surface is worse than volume alone.
Discord capture guidelines (pin this)
- Ask testers to state
build_idaloud at clip start. - Prefer 30–90 second clips over hour-long sessions for triage.
- Push-to-talk where possible.
- One bug per clip when feasible.
- No facecam required—audio-first is fine.
Reduces Whisper confusion and summary edit time.
BUILD_RECEIPT column extension
Add optional columns to your receipt CSV/JSON:
| Column | Example |
|---|---|
playtest_vod_batch |
2026-05-24 |
clips_transcribed |
5 |
p0_from_vod |
1 |
whisper_model |
small |
Proves feedback loop ran when Q3 diligence asks how playtests inform builds.
Engine-agnostic bug hints from speech
| Player says | Often means |
|---|---|
| “frozen” | Soft-lock, not always crash |
| “save gone” | Slot / cloud / path bug—see save-slot case study |
| “demo ended early” | Scope mismatch—store-demo mismatch |
| “too loud” | Audio—LUFS listicle |
| “F12” | Debug surface—dev console opinion |
Transcripts make these searchable across dozens of clips.
Pro tips (eight)
- Keep a vocab.txt (
roguelike, character names) for post-edit find-replace. - Run Whisper on muted sections anyway—players often narrate over silence.
- Store SRT if you need timeline UI—Whisper can emit subtitles.
- Do not auto-close issues from ASR alone—human confirms repro.
- Tag duplicate transcripts with same
issue_id. - Archive failed ffmpeg clips separately—corrupt upload debug.
- For Steam Playtest, match
build_idto depot label from isolation map. - Pre-fest: run one batch on golden path clip to train reviewers.
Month-one adoption ladder
| Week | Goal |
|---|---|
| 1 | One clip E2E + receipt |
| 2 | Batch 5 clips; open 3 issues |
| 3 | Add surface column to board |
| 4 | Wire BUILD_RECEIPT column; delete inbox per retention |
Common mistakes (seven)
- Cloud-uploading full VODs without consent.
- Skipping
build_idin filenames. - Trusting Whisper game terms without human edit.
- Mixing surfaces in one fix sprint.
- No retention policy — legal debt.
- Transcript without summary — still unsearchable for producers.
- Chasing clip #40 before fixing P0 from clip #3.
Privacy and consent (non-legal)
| Topic | Pipeline action |
|---|---|
| Voice in clip | Mention in playtest signup |
| Streamer VOD | Separate consent |
| Retention | README states days |
| Cloud AI | Opt-in text-only only |
| Deletion | Delete wav/mp4; keep summary |
Consult counsel for commercial release—workflow stays local-first by default.
Pairing with fest ops cluster
- Save-slot case study — transcript may catch “wrong save label”
- Fab pipeline pressure — do not prioritize art from VOD mood
- Fest cap — contractor time for triage vs trailer
- Menu FPS opinion — clips may mention coil whine—tag
performance
Accuracy limits (honest)
| Limit | Mitigation |
|---|---|
| Crosstalk in Discord | Push push-to-talk guideline |
| Game SFX over voice | --no_speech_threshold tuning; manual review |
| Non-English | Set --language correctly |
| Slang / lore terms | Custom vocab list in post-edit |
Whisper is triage, not courtroom evidence—repro still needs save file or build_label confirm.
Snippet-friendly answers
Should I upload playtest VODs to cloud transcription?
If legal approves the OpenAI Whisper API, segment files before upload—see 15 Free Whisper API chunking and local fallback tools and 413 Payload Too Large fix.
Default no—run Whisper locally; upload text summaries only if consented.
Which Whisper model for playtest?
small GPU or base CPU for most indie batches.
How does this relate to playtest isolation?
Tag surface on every summary; never merge Playtest bugs into fest_public without review.
Key takeaways
- Mid-2026 Discord clip volume needs local triage, not heroic rewatching.
- Pipeline: ffmpeg → Whisper → summary template → board → receipt.
playtest_vod_triage_receipt_v1.jsondocuments batches.- Gates T1–T6 include surface tagging.
- Filenames must carry
build_idandsurface. - Not narrative voice notes or ElevenLabs NPC architecture.
- Optional Ollama on text only under consent.
- Weekly ritual pairs Wednesday smoke and BUILD_RECEIPT.
- Retention policy in
playtest-vod/README.md. - Whisper
smalldefault;tinyfor pipeline smoke. - Human edits game terms after ASR.
- 18 playtest tools complement structured forms.
- Cloud upload flag stays false in receipt unless policy changes.
- Fulfills forward link from playtest isolation.
- Windows: run Whisper in WSL2 if native CUDA path is painful—still local disk.
- No Steam metadata checklist content.
FAQ
OBS vs Discord native clips?
Both work—normalize with ffmpeg to 16 kHz wav.
No GPU?
Use base overnight; process fewer clips per batch.
Can I transcribe fest stream VODs?
Yes—tag surface: fest_public; separate fix list.
Export to Jira?
Map summary fields to issue description; attach transcript path as attachment or link to internal git LFS if size allows.
Whisper hallucination on silence?
Trim leading silence with ffmpeg -ss or use VAD-friendly settings; review empty-gameplay clips manually.
Duplicate reports across clips?
Merge issues; link multiple transcript files.
Mac paths?
Same pipeline; install ffmpeg via Homebrew.
Team of one—worth it?
Yes if you receive more than three clips per week; skip if playtests are purely written surveys—use 18 playtest tools forms instead.
Conclusion
Playtest feedback buried in video is schedule risk before October. Local Whisper turns clips into searchable, tagged, build-linked text without shipping player VODs to the cloud.
Set up folders tonight. Process five clips only after consent README is pinned. File the receipt. Fix P1 before browsing Fab sales.
Workflow close: The cost of not transcribing is every producer hour rewatching the same soft-lock at 4:12—Whisper pays for itself on the first duplicate report you avoid.
Next reads: Lesson 200 — concat_ok gate before Whisper batch (course), OBS Replay Buffer concat evening pipeline, Whisper CUDA silent CPU fallback on Windows 11 (help), 15 Free Whisper/ffmpeg playtest VOD tools (resource list), Playtest isolation playbook, BUILD_RECEIPT pipeline.