{
    "componentChunkName": "component---src-pages-blog-mdx-slug-js",
    "path": "/blog/2026-06-12-building-the-subtitle-tool/",
    "result": {"data":{"mdx":{"frontmatter":{"title":"Building a Live Subtitle Tool With Mistral","tags":["AI","Mistral","voice","machine learning","Voxtral"],"categories":[],"date":"June 12, 2026","image":null,"imageAlt":null},"id":"fb5e6f8e-7da3-506b-b50c-869e8bdde883","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Building a Live Subtitle Tool With Mistral\",\n  \"date\": \"2026-06-12T00:00:00.000Z\",\n  \"tags\": [\"AI\", \"Mistral\", \"voice\", \"machine learning\", \"Voxtral\"],\n  \"categories\": [],\n  \"draft\": false\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"A few weeks ago I wrote about going through a Mistral course and, at the end, listed a few project ideas I kept coming back to. One of them was a closed captioning and subtitle tool: transcribe audio, clean it up, add timecodes, maybe translate.\"), mdx(\"p\", null, \"Funny enough, the first real project in the course turned out to be almost exactly that: a subtitle and transcription app, built to the instructor's spec. I didn't come up with the idea myself this time, but it's close enough to what I'd been imagining that working through it felt like getting a sneak preview of my own list.\"), mdx(\"p\", null, \"The result is a working app: live subtitles from your microphone, plus a separate panel for uploading an audio file and getting back a full transcript with timestamps, speaker labels, and per-word timing. I had Claude build it from the instructor's spec, and I want to walk through how the different pieces fit together, because some of them surprised me. The full code is on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/gjack/live-subtitling\"\n  }, \"GitHub\"), \" if you want to follow along or poke at it yourself.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"Two Very Different Problems Wearing One UI\"), mdx(\"p\", null, \"The app looks like a single page, but it's really two unrelated problems stapled together.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Realtime subtitling\"), \" is a streaming problem. Audio comes in continuously from your microphone, and the model has to guess at words before it's heard the whole sentence, then revise itself as more audio arrives. It's a WebSocket, an open connection, partial results that get overwritten.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Offline transcription\"), \" is a batch problem. You hand over a complete audio file and get back a complete answer: segments, speakers, word timings, all at once. No partial anything.\"), mdx(\"p\", null, \"Both use the same underlying Voxtral model family, but the SDK shapes for talking to them are completely different, and so is the code. It's worth treating them separately, which is also how the spec was written.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"The Realtime Side: Two Tasks Talking Past Each Other\"), mdx(\"p\", null, \"The realtime path is a FastAPI WebSocket endpoint that sits in the middle between your browser and Mistral's realtime transcription endpoint. The browser doesn't talk to Mistral directly; it talks to this server, and the server relays.\"), mdx(\"p\", null, \"The first message the browser sends isn't audio, it's configuration:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"javascript\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function-variable function\"\n  }, \"onopen\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"async\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=>\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n  ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"send\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token constant\"\n  }, \"JSON\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"stringify\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token literal-property property\"\n  }, \"latency\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \":\"), \" latencySel\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"value \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"setStatus\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"connecting\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"startCapture\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\")))), mdx(\"p\", null, \"That \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"latency\"), \" value is the dual-delay trick from the spec: a dropdown with three presets, fast (240ms), balanced (480ms), and accurate (2.4s). It's a real tradeoff, not a cosmetic setting. Lower delay means subtitles appear almost instantly but the model has less audio context to work with, so it occasionally has to walk back what it said. Higher delay means the model waits longer before committing to a transcript, so what it produces is more accurate but feels laggier. The server maps that choice straight onto a Voxtral parameter:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"latency_map \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"fast\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"240\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"balanced\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"480\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"accurate\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"2400\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\ndelay_ms \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" latency_map\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"get\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"config\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"get\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"latency\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"balanced\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"480\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\\nconnection \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" client\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"audio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"realtime\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"connect\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"\\n    model\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voxtral-mini-transcribe-realtime-2602\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    audio_format\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"AudioFormat\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"encoding\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"pcm_s16le\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" sample_rate\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"16000\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    target_streaming_delay_ms\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"delay_ms\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, \"Once that connection is open, the server is juggling two streams of data at once: audio coming \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"in\"), \" from the browser, and transcript events going \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"out\"), \" from Mistral. Those don't happen on the same schedule, so they're handled as two independent async tasks running side by side:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"receive_task \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"ensure_future\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"receive_audio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\nstream_task \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"ensure_future\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"stream_events\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"await\"), \" asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"wait\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"receive_task\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" stream_task\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" return_when\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"asyncio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"FIRST_COMPLETED\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"receive_audio\"), \" just shovels raw PCM bytes from the browser to Mistral as fast as they arrive. \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"stream_events\"), \" listens for whatever Mistral sends back and relays it the other way. The \\\"first completed\\\" wait matters because either side can end the conversation (the user clicking Stop ends \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"receive_audio\"), \", while a connection error would end \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"stream_events\"), \"), and whichever one finishes first kicks off cleanup for both.\"), mdx(\"p\", null, \"What Mistral sends back comes in two flavors, and the distinction is the whole UX of the subtitle experience. \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"TranscriptionStreamTextDelta\"), \" events are partial, provisional text: words the model thinks it heard but hasn't committed to yet. \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"TranscriptionStreamSegmentDelta\"), \" events are final, a complete chunk of speech with start and end timestamps that won't change. The frontend treats these completely differently:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"javascript\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function-variable function\"\n  }, \"onmessage\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token parameter\"\n  }, \"e\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=>\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" msg \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token constant\"\n  }, \"JSON\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"parse\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"e\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"data\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"msg\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"type \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"===\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"text_delta\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    partialEl\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"textContent \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"+=\"), \" msg\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"text\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"else\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"msg\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"type \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"===\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"segment\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"appendSegment\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"msg\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    partialEl\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"textContent \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\")))), mdx(\"p\", null, \"Partial text appends into a little preview area, which is the \\\"tentative subtitle\\\" effect, text that flickers and grows while you're still talking. The moment a segment arrives, that preview gets wiped and the finalized line drops into the permanent transcript panel with its timestamp. Watching this live is oddly satisfying: there's a visible texture to it, the rough draft dissolving into the clean version a beat later.\"), mdx(\"p\", null, \"One detail I didn't expect: getting audio out of the browser in the right format takes more plumbing than I assumed. Mistral wants 16kHz mono PCM16, but \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"getUserMedia\"), \" gives you 32-bit floating point samples at whatever rate the hardware feels like. So there's a manual conversion step on every audio buffer:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"javascript\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"scriptNode\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function-variable function\"\n  }, \"onaudioprocess\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token parameter\"\n  }, \"e\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=>\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" float32 \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" e\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"inputBuffer\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"getChannelData\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" int16 \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"new\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token class-name\"\n  }, \"Int16Array\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"float32\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"length\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"for\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"let\"), \" i \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"0\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \" i \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"<\"), \" float32\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"length\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \" i\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"++\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"let\"), \" v \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" float32\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"i\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"*\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"32768\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"v \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \">\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"32767\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" v \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"32767\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"v \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"<\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"-\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"32768\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" v \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"-\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token number\"\n  }, \"32768\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    int16\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"i\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" v\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\n  ws\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"send\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"int16\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"buffer\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\")))), mdx(\"p\", null, \"This callback fires every time the script node has accumulated another 4096-sample chunk from the microphone, which at 16kHz works out to about four times a second. The Web Audio API hands you that chunk as a \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"Float32Array\"), \", where every sample is a number between -1.0 and 1.0 representing the waveform's amplitude at that instant, regardless of what the underlying hardware actually uses.\"), mdx(\"p\", null, \"Voxtral wants something different: 16-bit signed integers (\", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"pcm_s16le\"), \"), where the range is -32768 to 32767 instead of -1.0 to 1.0. The loop rescales each sample by multiplying by 32768, which maps -1.0 to -32768 and 1.0 to... 32768, one more than the format allows. That edge case is why the clamping lines exist; a sample sitting exactly at the top of the float range would otherwise overflow and wrap around to a loud click in the audio. Once clamped, assigning the value into an \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"Int16Array\"), \" slot truncates it to an integer, and \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"int16.buffer\"), \" gives you the raw bytes behind that array, ready to ship over the WebSocket as a binary frame.\"), mdx(\"p\", null, \"It's a small loop, but it's a reminder that \\\"send audio to an AI model\\\" still involves the same low-level signal wrangling that's been part of audio programming forever. The model is new. The bit-shuffling to feed it is not.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"The Offline Side: Where the Real Gotchas Live\"), mdx(\"p\", null, \"The file upload half of the app felt, on paper, like it should be the simpler one. No streaming, no async tasks, no WebSocket. Upload a file, call an API, get a structured response back.\"), mdx(\"p\", null, \"In practice this is where most of the SDK's sharp edges were. None of them are exotic, they're just the kind of thing you only discover by trying it, which is exactly why having a spec written from someone else's hard-won experience was useful.\"), mdx(\"p\", null, \"The first one: the method is \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \".complete()\"), \", not \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \".create()\"), \". Small, but the kind of thing that costs you ten minutes of staring at a stack trace.\"), mdx(\"p\", null, \"The second: you can't hand the SDK a raw file or a \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"BytesIO\"), \". It wants a \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"MistralFile\"), \" wrapper:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"result \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" client\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"audio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"transcriptions\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"complete\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"\\n    model\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"voxtral-mini-latest\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token builtin\"\n  }, \"file\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"MistralFile\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"fileName\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token builtin\"\n  }, \"file\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"filename\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" content\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \"contents\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n    timestamp_granularities\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"segment\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, \"(And since FastAPI \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"also\"), \" has something called \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"File\"), \", the server imports it as \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"FastAPIFile\"), \" to keep the two from colliding, a small naming collision that would otherwise be very confusing to debug.)\"), mdx(\"p\", null, \"The third gotcha is the one that shapes the whole endpoint's structure: \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"timestamp_granularities\"), \" only accepts one value at a time. You'd think \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"[\\\"segment\\\", \\\"word\\\"]\"), \" would give you both segment-level and word-level timing in one response, but the SDK concatenates the list into a single string and the request comes back malformed. If you want both, and the spec calls for both as separate optional checkboxes, you make two separate calls:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"python\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-python\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"seg_kwargs \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"**\"), \"common_kwargs\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"timestamp_granularities\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"segment\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" diarize\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    seg_kwargs\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"diarize\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token boolean\"\n  }, \"True\"), \"\\nresult \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" client\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"audio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"transcriptions\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"complete\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"**\"), \"seg_kwargs\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n\\nwords \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" word_timestamps\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n    word_result \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" client\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"audio\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"transcriptions\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"complete\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"\\n        \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"**\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"**\"), \"common_kwargs\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"timestamp_granularities\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"word\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"for\"), \" seg \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"in\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"word_result\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"segments \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"or\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \"\\n        words\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"append\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"text\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" seg\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"text\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"start\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" seg\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"start\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \",\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token string\"\n  }, \"\\\"end\\\"\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \":\"), \" seg\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"end\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\")))), mdx(\"p\", null, \"That second call has its own twist: when you ask for word-level granularity, the response doesn't give you segments with a nested list of words inside them. Each \\\"segment\\\" in that response \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"is\"), \" a single word. So the same \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \".segments\"), \" field means something structurally different depending on what you asked for: same shape, same field names, different meaning. It's the kind of inconsistency that's easy to miss until your word timestamps come out as one giant blob covering the entire file.\"), mdx(\"p\", null, \"Diarization had its own small trap: turning on \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"diarize=True\"), \" without also requesting segment-level timestamps gets you a 422 error, because speaker labels are attached to segments and the API has nothing to attach them to otherwise. When it works, each segment comes back with a \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"speaker_id\"), \" like \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"\\\"speaker_1\\\"\"), \" or \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \"\\\"speaker_2\\\"\"), \", which the frontend turns into color-coded chips:\"), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"javascript\"\n  }, mdx(\"pre\", {\n    parentName: \"div\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"function\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"speakerClass\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token parameter\"\n  }, \"speakerId\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"if\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"!\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"speakerId \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"in\"), \" speakerMap\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"{\"), \"\\n    \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"const\"), \" idx \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" Object\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token function\"\n  }, \"keys\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"(\"), \"speakerMap\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \")\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"length \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"%\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token constant\"\n  }, \"SPEAKER_COLORS\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \".\"), \"length\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n    speakerMap\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"speakerId\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token operator\"\n  }, \"=\"), \" \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token constant\"\n  }, \"SPEAKER_COLORS\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"idx\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\"), \"\\n  \", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token keyword\"\n  }, \"return\"), \" speakerMap\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"[\"), \"speakerId\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"]\"), mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \";\"), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"token punctuation\"\n  }, \"}\")))), mdx(\"p\", null, \"I tested diarization with a recording of my husband and me pretending to do an interview, taking turns asking and answering questions. The model correctly tagged each of us as a separate speaker for the whole recording. It worked exactly as expected, but it also left me with questions the test didn't answer. We were careful to take turns; what happens if we talk over each other? What if, instead of talking, we were singing? And if I did the whole recording myself but changed my voice for each \\\"character,\\\" would the model still call that two speakers, or would it recognize both as me? Diarization clearly works for the clean, polite-conversation case. I have no idea where its edges are.\"), mdx(\"p\", null, \"There's also context biasing, a comma-separated list of words you expect to show up (names, jargon, brand names) that nudges the model toward recognizing them correctly. It's offline-only; the realtime \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \".connect()\"), \" call doesn't accept it. I tried it on a recording of myself talking about using Mistral's voice transcription models. I speak English with a Hispanic accent, and without context biasing, \\\"voice\\\" kept coming back as \\\"both\\\", close enough phonetically that the model picked the wrong common word. Adding \\\"voice\\\" and \\\"models\\\" to the context bias list was enough to fix it.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"What It's Like to Use\"), mdx(\"p\", null, mdx(\"span\", {\n    parentName: \"p\",\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"600px\"\n    }\n  }, \"\\n      \", mdx(\"a\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-link\",\n    \"href\": \"/static/70ad802e47f0ff4cb460a80f0bc41fbc/ed8a2/app-screenshot.png\",\n    \"style\": {\n      \"display\": \"block\"\n    },\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }, \"\\n    \", mdx(\"span\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"77.99999999999999%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB4klEQVQ4y5WUyW7bMBCGdQsQW5JliqQoiqJ2yYucJmldFGl7aFIgQG+59QF66SF9jB77yH9BeqkDb83hwwic4cxPaoZOFGeomh4675AVHZpugaKaYTSO4I0Y/IC/CifNWtzcfkQ3vcZ0fot37z9bCJVwfbq34VQR43MCwhCEBELmoFzBHQlcDsdn1Rm/64cYesTijVbFnUX/hG+Pv9FO3iIvZ5CqQ0BiG2A27bPaaGKEbFBUb5CVVyBMrxQmaonp9AmUF3aRiQIh1ydIreVxiURPUTY3KOprUJ7ZYk4QCoRCIkoqREm9Y2sI1RzF+LmswOLCWlPIKgyZhsrnkHqCRE+s3RDJyio5ivHLCpGsQZhaJ+QaadlD5TOkxfwFGyXnMHEmj01IWLqVv2vPHXmXOG3/JTR/i0YZKNcgVFmHkW/WX8NmEJyhRzFwQ4tp5IAI+IFx8i37PXgYP2BwhCxQt1eo24Vt7mPTscvJSflw9wnPv37iy8NXKN0hZGonKLKMQwVCU4ypst/++niHCjlVfYfl8gfK8h4sSqB0Ay5y0EiDGUQGqSZI8x6xMq1kJkkeVGoV5vk9Hh/+oJ9/x8D11/fxsrK5BjO3xno+3SY7lNQZ+gEuLi8wcL31Ef/vmTp2l38BU3KdlfLjdKUAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"The app's dark-themed interface, showing the realtime subtitling panel with Start/Stop/Clear controls and a latency dropdown, and the file transcription panel below with diarization and word timestamp options\",\n    \"title\": \"The app's dark-themed interface, showing the realtime subtitling panel with Start/Stop/Clear controls and a latency dropdown, and the file transcription panel below with diarization and word timestamp options\",\n    \"src\": \"/static/70ad802e47f0ff4cb460a80f0bc41fbc/0a47e/app-screenshot.png\",\n    \"srcSet\": [\"/static/70ad802e47f0ff4cb460a80f0bc41fbc/8a4e8/app-screenshot.png 150w\", \"/static/70ad802e47f0ff4cb460a80f0bc41fbc/5a46d/app-screenshot.png 300w\", \"/static/70ad802e47f0ff4cb460a80f0bc41fbc/0a47e/app-screenshot.png 600w\", \"/static/70ad802e47f0ff4cb460a80f0bc41fbc/1cfc2/app-screenshot.png 900w\", \"/static/70ad802e47f0ff4cb460a80f0bc41fbc/c1b63/app-screenshot.png 1200w\", \"/static/70ad802e47f0ff4cb460a80f0bc41fbc/ed8a2/app-screenshot.png 1972w\"],\n    \"sizes\": \"(max-width: 600px) 100vw, 600px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\",\n    \"decoding\": \"async\"\n  }), \"\\n  \"), \"\\n    \")), mdx(\"p\", null, \"The interface is intentionally plain: dark theme, two stacked panels, nothing that distracts from the transcript itself.\"), mdx(\"p\", null, \"Open the app, hit Start, and start talking. Within a second or so, words start appearing in the subtitle area: tentative, sometimes slightly wrong, occasionally getting silently corrected as more context arrives. A beat later, a finalized line with a timestamp drops into the transcript below. Switch the latency preset to \\\"accurate\\\" and the tentative text basically stops appearing; you wait longer, but what lands is cleaner.\"), mdx(\"p\", null, \"Drop a recording into the file upload section, tick \\\"speaker diarization,\\\" and the transcript comes back with each turn of conversation labeled and color-coded. Tick \\\"word timestamps\\\" too and you get every single word laid out with its own timing, like a karaoke track for your own recording.\"), mdx(\"p\", null, \"None of this is groundbreaking technology at this point. Transcription, diarization, and word timing all exist elsewhere. What stood out to me was how much of the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"implementation\"), \" is genuinely just plumbing: format conversions, two async loops politely taking turns, a method name that's \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \".complete()\"), \" instead of \", mdx(\"code\", {\n    parentName: \"p\",\n    \"className\": \"language-text\"\n  }, \".create()\"), \", a list parameter that secretly only wants one item. The model does the hard part. Getting bytes to it and back, in the right shape, at the right time, is where almost all the code lives.\"), mdx(\"hr\", null), mdx(\"h2\", null, \"What I'm Curious About Next\"), mdx(\"p\", null, \"Building this raised a question I don't have an answer to yet: could it translate as it goes?\"), mdx(\"p\", null, \"The pieces feel like they're in the neighborhood. Voxtral can already detect the language being spoken, and I've seen what Voxtral's voice cloning can do with cross-lingual speech, taking a few seconds of someone's voice and producing fluent output in a language they never spoke in the clip. So the ingredients, transcription, language detection, and cross-lingual voice synthesis, all exist somewhere in this model family.\"), mdx(\"p\", null, \"Whether they can be chained together in realtime, with subtitles in a second language appearing only moments behind the speaker, is a different question entirely. Realtime transcription is already a balancing act between latency and accuracy, as the dual-delay setting shows. Adding a translation step, and maybe a synthesis step on top of that, seems like it could multiply that delay rather than just add to it. I genuinely don't know if \\\"live translated subtitles\\\" is a reasonable next exercise or a much harder problem wearing a familiar costume. That's the thing I want to poke at next.\"));\n}\n;\nMDXContent.isMDXComponent = true;","timeToRead":7,"slug":"2026-06-12-building-the-subtitle-tool/"},"allMdx":{"edges":[{"next":{"slug":"2013-03-17-silicon-valley-devfest/","frontmatter":{"title":"Silicon Valley DevFest"},"id":"edcdad98-0f1c-5f08-b9bf-ef2584a6784d"},"previous":null,"node":{"id":"3378703f-653c-58fd-a11e-7adc675fb79e","frontmatter":{"title":"Makings of a Pythonista"},"slug":"2012-11-21-makings-of-a-pythonista"}},{"next":{"slug":"2015-06-14-in-search-of-a-faster-query","frontmatter":{"title":"In search of a faster query"},"id":"c6497654-5ee0-5b14-b735-e08bbbc6a4bf"},"previous":{"frontmatter":{"title":"Makings of a Pythonista"},"slug":"2012-11-21-makings-of-a-pythonista"},"node":{"id":"edcdad98-0f1c-5f08-b9bf-ef2584a6784d","frontmatter":{"title":"Silicon Valley DevFest"},"slug":"2013-03-17-silicon-valley-devfest/"}},{"next":{"slug":"2015-11-01-diary-of-a-dev-joys-of-building","frontmatter":{"title":"Diary of a Junior Dev: The joys of building"},"id":"ca6c84a6-3aea-5278-bf26-267d43acb227"},"previous":{"frontmatter":{"title":"Silicon Valley DevFest"},"slug":"2013-03-17-silicon-valley-devfest/"},"node":{"id":"c6497654-5ee0-5b14-b735-e08bbbc6a4bf","frontmatter":{"title":"In search of a faster query"},"slug":"2015-06-14-in-search-of-a-faster-query"}},{"next":{"slug":"2016-12-24-working-with-d3-based-chart-library/","frontmatter":{"title":"TIL: Working with a D3 based chart library"},"id":"776dcc9f-2ac4-511a-a9c6-982c226e0478"},"previous":{"frontmatter":{"title":"In search of a faster query"},"slug":"2015-06-14-in-search-of-a-faster-query"},"node":{"id":"ca6c84a6-3aea-5278-bf26-267d43acb227","frontmatter":{"title":"Diary of a Junior Dev: The joys of building"},"slug":"2015-11-01-diary-of-a-dev-joys-of-building"}},{"next":{"slug":"2017-03-15-react-conf-2017/","frontmatter":{"title":"React Conf 2017"},"id":"97c0196c-f0cd-5d6a-b2b9-fd3c6f1f1baa"},"previous":{"frontmatter":{"title":"Diary of a Junior Dev: The joys of building"},"slug":"2015-11-01-diary-of-a-dev-joys-of-building"},"node":{"id":"776dcc9f-2ac4-511a-a9c6-982c226e0478","frontmatter":{"title":"TIL: Working with a D3 based chart library"},"slug":"2016-12-24-working-with-d3-based-chart-library/"}},{"next":{"slug":"2017-04-12-the-initial-state-gotcha","frontmatter":{"title":"The Initial State Gotcha"},"id":"82041731-cb81-571d-958c-00f57d99b927"},"previous":{"frontmatter":{"title":"TIL: Working with a D3 based chart library"},"slug":"2016-12-24-working-with-d3-based-chart-library/"},"node":{"id":"97c0196c-f0cd-5d6a-b2b9-fd3c6f1f1baa","frontmatter":{"title":"React Conf 2017"},"slug":"2017-03-15-react-conf-2017/"}},{"next":{"slug":"2017-05-13-blue-socks-at-orange/","frontmatter":{"title":"Blue Socks Spotted at Orange"},"id":"893cf729-88c0-5706-bcb8-d14f75a30f2b"},"previous":{"frontmatter":{"title":"React Conf 2017"},"slug":"2017-03-15-react-conf-2017/"},"node":{"id":"82041731-cb81-571d-958c-00f57d99b927","frontmatter":{"title":"The Initial State Gotcha"},"slug":"2017-04-12-the-initial-state-gotcha"}},{"next":{"slug":"once-upon-a-blog/","frontmatter":{"title":"Once upon a Blog"},"id":"db5caf55-934b-5de1-aa6b-5a2078e4f869"},"previous":{"frontmatter":{"title":"The Initial State Gotcha"},"slug":"2017-04-12-the-initial-state-gotcha"},"node":{"id":"893cf729-88c0-5706-bcb8-d14f75a30f2b","frontmatter":{"title":"Blue Socks Spotted at Orange"},"slug":"2017-05-13-blue-socks-at-orange/"}},{"next":{"slug":"story-of-a-laptop-upgrade","frontmatter":{"title":"Story of a laptop upgrade"},"id":"91dabe95-0227-5d01-b1d1-f10959c5d43a"},"previous":{"frontmatter":{"title":"Blue Socks Spotted at Orange"},"slug":"2017-05-13-blue-socks-at-orange/"},"node":{"id":"db5caf55-934b-5de1-aa6b-5a2078e4f869","frontmatter":{"title":"Once upon a Blog"},"slug":"once-upon-a-blog/"}},{"next":{"slug":"the-code-we-write-today/","frontmatter":{"title":"The code we write today"},"id":"08584458-be98-53c2-8cf3-9fa51060e04b"},"previous":{"frontmatter":{"title":"Once upon a Blog"},"slug":"once-upon-a-blog/"},"node":{"id":"91dabe95-0227-5d01-b1d1-f10959c5d43a","frontmatter":{"title":"Story of a laptop upgrade"},"slug":"story-of-a-laptop-upgrade"}},{"next":{"slug":"rails-on-docker/","frontmatter":{"title":"Rails on Docker"},"id":"5e92911a-24f7-562d-8ecd-9a7391960489"},"previous":{"frontmatter":{"title":"Story of a laptop upgrade"},"slug":"story-of-a-laptop-upgrade"},"node":{"id":"08584458-be98-53c2-8cf3-9fa51060e04b","frontmatter":{"title":"The code we write today"},"slug":"the-code-we-write-today/"}},{"next":{"slug":"rails-on-docker-part-two/","frontmatter":{"title":"Rails on Docker Part Two"},"id":"534e725d-71be-5e6f-ace7-75db51185701"},"previous":{"frontmatter":{"title":"The code we write today"},"slug":"the-code-we-write-today/"},"node":{"id":"5e92911a-24f7-562d-8ecd-9a7391960489","frontmatter":{"title":"Rails on Docker"},"slug":"rails-on-docker/"}},{"next":{"slug":"super-powers-for-active-record-relation","frontmatter":{"title":"Super Powers for ActiveRecord::Relation"},"id":"3d5d8b08-cf43-57be-8c43-aad95e2dfd06"},"previous":{"frontmatter":{"title":"Rails on Docker"},"slug":"rails-on-docker/"},"node":{"id":"534e725d-71be-5e6f-ace7-75db51185701","frontmatter":{"title":"Rails on Docker Part Two"},"slug":"rails-on-docker-part-two/"}},{"next":{"slug":"2026-05-30-outsmarting-github-copilot","frontmatter":{"title":"I Tried to Outsmart Copilot. It Made Me a Better Developer."},"id":"8bc48193-2ba7-5463-9edb-0f5e3a910bb1"},"previous":{"frontmatter":{"title":"Rails on Docker Part Two"},"slug":"rails-on-docker-part-two/"},"node":{"id":"3d5d8b08-cf43-57be-8c43-aad95e2dfd06","frontmatter":{"title":"Super Powers for ActiveRecord::Relation"},"slug":"super-powers-for-active-record-relation"}},{"next":{"slug":"2026-06-06-first-steps-with-mistral","frontmatter":{"title":"I Started a Mistral Tutorial. It Ended with My Voice Speaking French."},"id":"cb7d26e3-c28a-5c0a-a659-83f90d04bd15"},"previous":{"frontmatter":{"title":"Super Powers for ActiveRecord::Relation"},"slug":"super-powers-for-active-record-relation"},"node":{"id":"8bc48193-2ba7-5463-9edb-0f5e3a910bb1","frontmatter":{"title":"I Tried to Outsmart Copilot. It Made Me a Better Developer."},"slug":"2026-05-30-outsmarting-github-copilot"}},{"next":{"slug":"2026-06-12-building-the-subtitle-tool/","frontmatter":{"title":"Building a Live Subtitle Tool With Mistral"},"id":"fb5e6f8e-7da3-506b-b50c-869e8bdde883"},"previous":{"frontmatter":{"title":"I Tried to Outsmart Copilot. It Made Me a Better Developer."},"slug":"2026-05-30-outsmarting-github-copilot"},"node":{"id":"cb7d26e3-c28a-5c0a-a659-83f90d04bd15","frontmatter":{"title":"I Started a Mistral Tutorial. It Ended with My Voice Speaking French."},"slug":"2026-06-06-first-steps-with-mistral"}},{"next":null,"previous":{"frontmatter":{"title":"I Started a Mistral Tutorial. It Ended with My Voice Speaking French."},"slug":"2026-06-06-first-steps-with-mistral"},"node":{"id":"fb5e6f8e-7da3-506b-b50c-869e8bdde883","frontmatter":{"title":"Building a Live Subtitle Tool With Mistral"},"slug":"2026-06-12-building-the-subtitle-tool/"}}]}},"pageContext":{"id":"fb5e6f8e-7da3-506b-b50c-869e8bdde883","slug":"2026-06-12-building-the-subtitle-tool/","__params":{"slug":"2026-06-12-building-the-subtitle-tool"}}},
    "staticQueryHashes": ["2201243204"]}