<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/assets/rss.xsl" type="text/xsl"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"

>
<channel>
	<title>Sacha Chua - category - speech-recognition</title>
	<atom:link href="https://sachachua.com/blog/category/speech-recognition/feed/index.xml" rel="self" type="application/rss+xml" />
	<atom:link href="https://sachachua.com/blog/category/speech-recognition" rel="alternate" type="text/html" />
	<link>https://sachachua.com/blog/category/speech-recognition/feed/index.xml</link>
	<description>Emacs, sketches, and life</description>
  
	<lastBuildDate>Fri, 06 Nov 2026 15:50:05 GMT</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>11ty</generator>
  <item>
		<title>Clip: Emacs voice control - describing my setup during Linkarzu's livestream</title>
		<link>https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Thu, 29 Oct 2026 19:53:50 GMT</pubDate>
    <category>emacs</category>
<category>speech</category>
<category>speech-recognition</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/</guid>
		<description><![CDATA[<div class="media-post" id="orge3d3b0b">
<p>
</p><div class="yt-video"><iframe width="456" height="315" title="YouTube video player" src="https://www.youtube-nocookie.com/embed/Xk02luunlfc?enablejsapi=1" frameborder="0" allowfullscreen=""></iframe><a href="https://sachachua.com/blog/feed/Xk02luunlfc">Watch on YouTube</a></div>
<p></p>

<p>
linkarzu wins the clip-off thanks to his fancy AI-powered clipping process!
Here's a snippet from our conversation today.
He'll be posting more clips, and I'm also going to edit the transcript of the conversation.
Fun fun fun!
</p>

<p>
Additional notes:
</p>

<ul class="org-ul">
<li><a href="https://sachachua.com/dotemacs#navigation-substitution">Substitution</a>: I use <code>(add-hook 'substitute-post-replace-functions #'sacha-substitute-save-common-edits)</code> to save the changes. <a href="https://sachachua.com/dotemacs#navigation-substitution">sacha-substitute-save-common-edits</a>
<a href="https://sachachua.com/dotemacs#edit-text">Edit text</a> has functions like <code>sacha-subed-fix-common-errors-in-text</code> that apply those changes to my speech recognition results and <code>sacha-subed-fix-common-errors-from-start</code> that apply them to already-prepared transcripts. I really like how I can mark a region and use that to define the substitution, like changing "tug a" to "toggle".</li>

<li><a href="https://github.com/sachac/whisper.el/">sachac/whisper.el</a> has my fork of <a href="https://github.com/natrys/whisper.el">natrys/whisper.el</a> to support voice commands. <a href="https://codeberg.org/sachac/speech-input">speech-input</a> has a bunch of other things, too, and of course <a href="https://sachachua.com/dotemacs/">my Emacs config</a>.</li>

<li>I've been using <a href="https://github.com/ErikPrantare/phony.el">phony</a> to define more voice commands using a parser.</li>

<li>Apparently I cannot spell when thinking out loud. It is <a href="https://www.orgzlyrevived.com/">Orgzly Revived</a>, not ogrzly.</li>

<li>This actually doesn't use generative AI in the sense of Gemini or Claude, except for the conversational French demo. I use a local speech recognition model (currently <a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/emacsconf-subed.el">Orukeet</a>) and local speech synthesis (<a href="https://github.com/remsky/Kokoro-FastAPI">Kokoro FastAPI</a>), So depending on your feelings about large language models, the models might be small enough to be fine with you. Alternatively, there are classical approaches to both speech recognition and speech synthesis like <a href="https://kaldi-asr.org/">kaldi</a> and <a href="https://github.com/espeak-ng/espeak-ng">espeak-ng</a>.</li>
</ul>

<details class="code-details"><summary>Transcript and screenshots</summary>
<p>
</p><div class="full-transcript"><p></p><div class="transcript-heading"><span class="audio-time" data-start="0.000000">0:00</span> <strong>Voice commands that mishear</strong></div><p></p><span class="audio-time caption" data-speaker="Sacha" data-start="0.000000" data-stop="0.888000"><strong class="speaker-name">Sacha:</strong>  It turns out I cannot</span> <span class="audio-time caption" data-speaker="Sacha" data-start="0.948000" data-stop="3.229000">say start streaming reliably. Sometimes</span> <span class="audio-time caption" data-speaker="Sacha" data-start="3.289000" data-stop="4.599000">I say start screaming.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="4.600000" data-stop="6.218000"><strong class="speaker-name">Linkarzu:</strong>  Or like if I say Neovim,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="6.838000" data-stop="7.819000">it doesn't get it. It</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="7.839000" data-stop="9.559000">says "New of him".</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="9.952000" data-stop="10.553000">I want to take a</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="10.593000" data-stop="11.374000">look at what you were</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="11.414000" data-stop="12.276000">sharing a moment ago.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="14.579000" data-stop="16.121000">The speech to text,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="16.542000" data-stop="18.985000">and it happens in real time, right?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="18.986000" data-stop="20.287000"><strong class="speaker-name">Sacha:</strong>  Oh yeah, yeah.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="20.000000">0:20</span> <strong>How the live transcript works</strong></div><p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="20.499000" data-stop="21.880000"><strong class="speaker-name">Linkarzu:</strong>  But I don't do stuff</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="22.020000" data-stop="23.141000">real-time the way that</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="23.161000" data-stop="24.263000">you're doing right now.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="24.264000" data-stop="25.696000">The transcription happens</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="25.697000" data-stop="27.161000">when we're done with the call</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="27.162000" data-stop="29.344000">and the process starts automatically.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="30.005000" data-stop="31.446000">And a few hours after,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="31.586000" data-stop="32.506000">I have the clips in</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="32.526000" data-stop="33.727000">my dashboard that I can</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="33.847000" data-stop="35.448000">upload if I want, right?</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="35.468000" data-stop="37.489000">But it's not real-time and</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="37.509000" data-stop="39.170000">I'm like, oh, and I saw</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="39.851000" data-stop="40.911000">how you did it on</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="40.951000" data-stop="41.892000">your side and it's pretty</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="42.152000" data-stop="43.133000">accurate. Like,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="43.773000" data-stop="44.613000">what's the magic?</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="44.994000" data-stop="45.994000">What are you doing there?</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="46.375000" data-stop="47.315000">You're sending it</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="47.675000" data-stop="48.882000">somewhere, right?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="48.883000" data-stop="50.758000"><strong class="speaker-name">Sacha:</strong>  No, this is an entirely local</span> <span class="audio-time caption" data-speaker="Sacha" data-start="50.818000" data-stop="51.999000">model and I'm running</span> <span class="audio-time caption" data-speaker="Sacha" data-start="52.740000" data-stop="53.701000">it in a computer with</span> <span class="audio-time caption" data-speaker="Sacha" data-start="53.761000" data-stop="55.323000">hardly any GPU. So I'm</span> <span class="audio-time caption" data-speaker="Sacha" data-start="55.363000" data-stop="56.324000">actually just making it use</span> <span class="audio-time caption" data-speaker="Sacha" data-start="56.404000" data-stop="57.285000">CPU only</span> <span class="audio-time caption" data-speaker="Sacha" data-start="58.226000" data-stop="59.868000">for the speech recognition because</span> <span class="audio-time caption" data-speaker="Sacha" data-start="59.888000" data-stop="62.531000">I have a Lenovo P52 from</span> <span class="audio-time caption" data-speaker="Sacha" data-start="62.571000" data-stop="63.091000">2018, I think.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="64.773000" data-stop="65.734000">So it doesn't</span> <span class="audio-time caption" data-speaker="Sacha" data-start="66.455000" data-stop="67.798000">do this GPU thing very well.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="67.799000" data-stop="68.817000">I think you're usually</span> <span class="audio-time caption" data-speaker="Sacha" data-start="68.857000" data-stop="70.738000">On a Mac, so you're</span> <span class="audio-time caption" data-speaker="Sacha" data-start="70.798000" data-stop="71.899000">spoiled for choices there.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="72.320000" data-stop="73.861000">But speech recognition has gotten</span> <span class="audio-time caption" data-speaker="Sacha" data-start="74.021000" data-stop="75.442000">really good over the last</span> <span class="audio-time caption" data-speaker="Sacha" data-start="75.483000" data-stop="76.103000">few years.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="76.583000" data-stop="78.025000">I'm using Orukeet at</span> <span class="audio-time caption" data-speaker="Sacha" data-start="78.065000" data-stop="78.545000">the moment.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="78.546000" data-stop="79.727000">It's a multilingual model,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="79.728000" data-stop="80.667000">so it can handle my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="81.027000" data-stop="82.529000">poor French pronunciations</span> <span class="audio-time caption" data-speaker="Sacha" data-start="83.369000" data-stop="84.470000">just as fine as my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="84.510000" data-stop="85.031000">English ones.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="85.791000" data-stop="86.732000">I can press my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="87.033000" data-stop="87.882000">keyboard shortcut.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="87.883000" data-stop="88.554000">For example,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="88.594000" data-stop="89.114000">I can say,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="90.015000" data-stop="90.896000">this is a test during</span> <span class="audio-time caption" data-speaker="Sacha" data-start="90.916000" data-stop="91.877000">a chat with linkarzu.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="93.612000" data-stop="94.974000">And then, because</span> <span class="audio-time caption" data-speaker="Sacha" data-start="94.975000" data-stop="97.094000">I've modified my dictionary,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="97.114000" data-stop="99.375000">your channel name actually shows</span> <span class="audio-time caption" data-speaker="Sacha" data-start="99.455000" data-stop="101.496000">up properly. Oh, okay. Okay,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="101.516000" data-stop="103.417000">okay, okay. We are talking</span> <span class="audio-time caption" data-speaker="Sacha" data-start="103.497000" data-stop="105.786000">about speech-to-text, dictation,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="105.787000" data-stop="105.814000">all sorts of other things like that.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="105.815000" data-stop="109.080000">Oh, where's my phone?</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="111.601000" data-stop="113.403000"><strong class="speaker-name">Linkarzu:</strong>  Okay, yeah, so it just</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="113.423000" data-stop="113.715000">puts it in.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="113.716000" data-stop="114.424000">It's pretty accurate.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="114.805000" data-stop="116.293000">It's extremely accurate.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="116.294000" data-stop="117.187000">What's the name</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="117.228000" data-stop="118.849000">you said? Because I use</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="118.889000" data-stop="120.751000">Wispr Flow, a local model as</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="120.791000" data-stop="122.213000">well. I do the transcript</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="122.253000" data-stop="124.135000">locally on my Mac. Wispr Flow</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="124.255000" data-stop="125.417000">is what the AI decided</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="125.437000" data-stop="126.157000">to use and I'm like,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="126.578000" data-stop="129.581000">oh, it gets it, but not...</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="130.562000" data-stop="131.823000"><strong class="speaker-name">Sacha:</strong>  Yeah, the model I'm currently</span> <span class="audio-time caption" data-speaker="Sacha" data-start="131.923000" data-stop="133.923000">using is called Orukeet.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="135.784000" data-stop="136.865000">I think it's based on</span> <span class="audio-time caption" data-speaker="Sacha" data-start="136.945000" data-stop="138.985000">Parakeet, but it's been tweaked</span> <span class="audio-time caption" data-speaker="Sacha" data-start="139.026000" data-stop="140.206000">for more performance</span> <span class="audio-time caption" data-speaker="Sacha" data-start="140.686000" data-stop="141.466000">and less memory.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="141.760000" data-stop="142.640000">But there are so many</span> <span class="audio-time caption" data-speaker="Sacha" data-start="142.820000" data-stop="143.928000">new models coming out.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="143.929000" data-stop="145.347000">It's really quite interesting</span> <span class="audio-time caption" data-speaker="Sacha" data-start="145.348000" data-stop="147.042000">because every day or every other day,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="147.062000" data-stop="148.122000">there's like, oh, try this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="148.182000" data-stop="149.203000">new model.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="149.683000" data-stop="151.984000">I'm not good enough to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="152.025000" data-stop="152.745000">keep on top of all</span> <span class="audio-time caption" data-speaker="Sacha" data-start="152.785000" data-stop="153.806000">those changes. If I can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="153.846000" data-stop="154.906000">manage to get the AI</span> <span class="audio-time caption" data-speaker="Sacha" data-start="154.946000" data-stop="155.647000">to generate me</span> <span class="audio-time caption" data-speaker="Sacha" data-start="156.027000" data-stop="157.068000">a server that has an</span> <span class="audio-time caption" data-speaker="Sacha" data-start="157.188000" data-stop="158.428000">OpenAI interface, I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="158.468000" data-stop="159.609000">can plop it right into</span> <span class="audio-time caption" data-speaker="Sacha" data-start="159.649000" data-stop="160.470000">my Emacs setup</span> <span class="audio-time caption" data-speaker="Sacha" data-start="160.870000" data-stop="161.930000">and it will just work.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="162.151000" data-stop="163.231000">So I'm happy with that.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="163.972000" data-stop="165.921000">And this is fast enough</span> <span class="audio-time caption" data-speaker="Sacha" data-start="165.922000" data-stop="167.374000">that I can use it to think</span> <span class="audio-time caption" data-speaker="Sacha" data-start="167.854000" data-stop="168.694000">and then when I have</span> <span class="audio-time caption" data-speaker="Sacha" data-start="168.714000" data-stop="169.255000">the text,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="169.635000" data-stop="171.096000">I can rearrange it however</span> <span class="audio-time caption" data-speaker="Sacha" data-start="171.176000" data-stop="171.636000">I'd like.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="172.000000">2:52</span> <strong>Playing with voice commands</strong></div><p></p><span class="audio-time caption" data-speaker="Sacha" data-start="172.036000" data-stop="172.897000"><strong class="speaker-name">Sacha:</strong> So for example,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="173.357000" data-stop="174.398000">"prepare to fast forward."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="176.625000" data-stop="177.305000">We're here.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="177.665000" data-stop="178.785000"><strong class="speaker-name">Linkarzu:</strong>  I have a similar command.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="179.053000" data-stop="179.687000">Let me see if it</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="179.707000" data-stop="180.347000">works. Hold on.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="181.007000" data-stop="183.528000">You see my terminal right now, right?</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="185.688000" data-stop="186.368000">Yes. I don't know if</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="186.408000" data-stop="187.168000">you can see my screen.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="187.396000" data-stop="188.396000">Virgin mode off.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="189.436000" data-stop="191.197000"><strong class="speaker-name">Sacha:</strong>  Mm hmm. Yeah, yeah, yeah.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="191.297000" data-stop="191.777000">I saw that.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="191.815000" data-stop="192.433000"><strong class="speaker-name">Linkarzu:</strong>  It didn't work.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="193.213000" data-stop="196.854000">But it changes my wallpaper and</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="196.914000" data-stop="197.874000">it changes my...</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="199.343000" data-stop="200.304000"><strong class="speaker-name">Sacha:</strong>  I have one too.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="200.704000" data-stop="201.084000">Laser.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="201.610000" data-stop="202.430000">Look, I changed</span> <span class="audio-time caption" data-speaker="Sacha" data-start="202.450000" data-stop="203.471000">my cursor to a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="203.511000" data-stop="204.792000">laser so I can say,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="204.852000" data-stop="205.892000">here's my mouse</span> <span class="audio-time caption" data-speaker="Sacha" data-start="205.932000" data-stop="206.573000">cursor, right?</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="206.953000" data-stop="209.855000"><strong class="speaker-name">Linkarzu:</strong>  Okay. Pretty cool.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="211.344000" data-stop="212.384000"><strong class="speaker-name">Sacha:</strong>  Yeah, so the nice thing</span> <span class="audio-time caption" data-speaker="Sacha" data-start="212.424000" data-stop="212.805000">about it...</span> <span class="audio-time caption" data-speaker="Sacha" data-start="213.265000" data-stop="214.185000">Oh, it's still listening.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="214.206000" data-stop="216.567000">Okay.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="217.208000" data-stop="217.948000">Let me just turn off</span> <span class="audio-time caption" data-speaker="Sacha" data-start="217.988000" data-stop="218.909000">laser mode here. Oh,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="219.489000" data-stop="219.929000">ah!</span> <span class="audio-time caption" data-speaker="Sacha" data-start="220.850000" data-stop="222.717000">Laser. Laser. Laser.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="223.816000" data-stop="224.657000"><strong class="speaker-name">Linkarzu:</strong>  It happens to me as</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="224.697000" data-stop="225.170000">well.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="225.171000" data-stop="228.580000"><strong class="speaker-name">Sacha:</strong>  Edit, retry. Laser.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="229.601000" data-stop="230.602000">Yeah, okay, there you go.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="231.000000">3:51</span> <strong>Speech commands enable more shortcuts</strong></div><p></p><span class="audio-time caption" data-speaker="Sacha" data-start="231.463000" data-stop="232.844000"><strong class="speaker-name">Sacha:</strong> The nice thing about</span> <span class="audio-time caption" data-speaker="Sacha" data-start="232.904000" data-stop="234.486000">speech commands is that it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="234.606000" data-stop="235.887000">opens up a whole lot</span> <span class="audio-time caption" data-speaker="Sacha" data-start="235.927000" data-stop="237.609000">of other ways to run</span> <span class="audio-time caption" data-speaker="Sacha" data-start="237.669000" data-stop="239.450000">commands without necessarily having to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="239.511000" data-stop="240.832000">memorize keyboard shortcuts.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="241.232000" data-stop="241.993000">And you can have more</span> <span class="audio-time caption" data-speaker="Sacha" data-start="242.093000" data-stop="242.914000">fun with it too.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="243.274000" data-stop="244.475000">So for example, now I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="244.515000" data-stop="245.196000">have this command.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="246.817000" data-stop="247.717000">It turns out I cannot</span> <span class="audio-time caption" data-speaker="Sacha" data-start="247.777000" data-stop="250.058000">say "start streaming" reliably. Sometimes</span> <span class="audio-time caption" data-speaker="Sacha" data-start="250.118000" data-stop="252.198000">I say start screaming. Or</span> <span class="audio-time caption" data-speaker="Sacha" data-start="252.238000" data-stop="253.099000">at least that's... Well,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="253.459000" data-stop="254.539000">I know sometimes I accidentally</span> <span class="audio-time caption" data-speaker="Sacha" data-start="254.579000" data-stop="255.559000">say that and sometimes that's</span> <span class="audio-time caption" data-speaker="Sacha" data-start="255.599000" data-stop="257.380000">what the speech recognition hears.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="257.740000" data-stop="258.940000">So instead, my command for</span> <span class="audio-time caption" data-speaker="Sacha" data-start="259.000000" data-stop="260.160000">starting this stream,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="260.680000" data-stop="262.341000">because of course Emacs controls</span> <span class="audio-time caption" data-speaker="Sacha" data-start="262.421000" data-stop="263.961000">my OBS through WebSockets,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="264.441000" data-stop="265.641000">is "let's go live".</span> <span class="audio-time caption" data-speaker="Sacha" data-start="266.142000" data-stop="267.042000">And my command</span> <span class="audio-time caption" data-speaker="Sacha" data-start="267.482000" data-stop="269.162000">for stopping the stream is</span> <span class="audio-time caption" data-speaker="Sacha" data-start="269.362000" data-stop="270.042000">"over and out".</span> <span class="audio-time caption" data-speaker="Sacha" data-start="270.663000" data-stop="271.543000">And just because it's a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="271.563000" data-stop="272.523000">little more fun to say,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="272.563000" data-stop="273.523000">it becomes easier for me</span> <span class="audio-time caption" data-speaker="Sacha" data-start="273.543000" data-stop="274.684000">me to remember what I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="274.724000" data-stop="275.524000">need to say in order</span> <span class="audio-time caption" data-speaker="Sacha" data-start="275.544000" data-stop="276.184000">to make this happen.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="277.000000">4:37</span> <strong>Does it handle hard words?</strong></div><p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="277.364000" data-stop="278.225000"><strong class="speaker-name">Linkarzu:</strong>  Yeah, yeah, yeah.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="278.685000" data-stop="278.865000">Now,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="279.585000" data-stop="280.926000">why is it accurate? And</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="281.333000" data-stop="282.733000">you have a dictionary of</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="282.873000" data-stop="283.594000">like the</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="285.494000" data-stop="286.815000">difficult words. Like if I</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="286.835000" data-stop="287.415000">say Neovim,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="288.035000" data-stop="289.016000">it doesn't get it. It</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="289.036000" data-stop="290.756000">says "New of him",</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="291.136000" data-stop="292.597000">or it just puts whatever</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="292.677000" data-stop="294.077000">it wants, whenever it wants.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="294.117000" data-stop="295.698000">Sometimes it puts something random</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="295.858000" data-stop="296.238000">out there.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="296.718000" data-stop="297.579000">What do you do for</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="297.599000" data-stop="298.805000">yours?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="298.806000" data-stop="301.360000"><strong class="speaker-name">Sacha:</strong>  "Neovim is okay, I guess."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="302.235000" data-stop="302.995000">Let's see if it does</span> <span class="audio-time caption" data-speaker="Sacha" data-start="303.075000" data-stop="304.780000">a thing. Yeah, it got</span> <span class="audio-time caption" data-speaker="Sacha" data-start="304.800000" data-stop="305.801000">that without my needing to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="305.821000" data-stop="306.821000">correct things. Let's pick a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="306.881000" data-stop="307.582000">harder word.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="310.184000" data-stop="311.805000">Anti-disestablishmentarianism.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="312.770000" data-stop="314.292000">Close enough. Okay, you know,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="314.312000" data-stop="315.653000">that requires very little</span> <span class="audio-time caption" data-speaker="Sacha" data-start="315.753000" data-stop="317.094000">editing. What's a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="317.194000" data-stop="319.655000">difficult word? Oh, actually, let's</span> <span class="audio-time caption" data-speaker="Sacha" data-start="319.675000" data-stop="321.057000">pick a different name.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="321.777000" data-stop="323.278000">Sometimes it gets Prot's name wrong.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="323.859000" data-stop="324.619000">"Prot does a lot of</span> <span class="audio-time caption" data-speaker="Sacha" data-start="324.659000" data-stop="325.580000">videos on Emacs."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="326.177000" data-stop="329.680000">Oh, no, it got him too.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="330.220000" data-stop="332.863000">If there was a thing that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="333.063000" data-stop="334.284000">is difficult to say...</span> <span class="audio-time caption" data-speaker="Sacha" data-start="334.651000" data-stop="335.812000">What was that? Okay.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="336.293000" data-stop="338.855000">"Heisenbug is making org.nvim."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="339.596000" data-stop="341.017000">That one. Okay, here we go.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="341.627000" data-stop="343.649000">"Heisenbug is making org.nvim."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="345.318000" data-stop="346.539000">I mean, it's getting him.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="351.000000">5:51</span> <strong>Thanking sponsors and members</strong></div><p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="351.225000" data-stop="352.133000"><strong class="speaker-name">Linkarzu:</strong>  Let me thank my</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="352.993000" data-stop="354.934000">YouTube members, the CEOs are</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="354.974000" data-stop="356.374000">shown on screen, web2three,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="356.634000" data-stop="358.034000">web2trhee.com, TED,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="358.354000" data-stop="360.835000">cpnllc.com, and also</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="361.195000" data-stop="361.535000">Gallo,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="361.915000" data-stop="363.235000">these are the three CEOs,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="363.395000" data-stop="364.015000">and I just want to</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="364.035000" data-stop="364.895000">thank the rest of the</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="364.915000" data-stop="365.575000">YouTube members,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="366.016000" data-stop="367.396000">which are the ones that are</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="367.456000" data-stop="368.316000">shown here, so</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="368.776000" data-stop="369.996000">just appreciate</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="370.516000" data-stop="371.496000">each one of the members</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="371.576000" data-stop="373.497000">for supporting the channel, okay?</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="374.000000">6:14</span> <strong>Fixing misrecognized words with a shortcut</strong></div><p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="374.248000" data-stop="377.473000"><strong class="speaker-name">Linkarzu:</strong>  So, it's a local model, interesting.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="377.493000" data-stop="378.454000">It is a local model.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="378.474000" data-stop="379.234000">Do you feed it a</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="379.274000" data-stop="380.073000">dictionary at some point?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="380.074000" data-stop="380.935000"><strong class="speaker-name">Sacha:</strong>  If I do need to change</span> <span class="audio-time caption" data-speaker="Sacha" data-start="380.975000" data-stop="382.216000">things... Okay, so if I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="382.276000" data-stop="383.177000">need to change things, let's</span> <span class="audio-time caption" data-speaker="Sacha" data-start="383.197000" data-stop="384.438000">say for example, Orgzly</span> <span class="audio-time caption" data-speaker="Sacha" data-start="384.478000" data-stop="385.478000">Revived, you probably can't get</span> <span class="audio-time caption" data-speaker="Sacha" data-start="385.498000" data-stop="385.818000">that one.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="386.359000" data-stop="387.479000">"I use Orgzly Revived on</span> <span class="audio-time caption" data-speaker="Sacha" data-start="387.519000" data-stop="388.460000">my phone to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="388.940000" data-stop="389.641000">check my tasks."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="390.583000" data-stop="391.504000">Oh yeah, so here, right?</span> <p></p><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-06-33-143.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-06-33-143.jpg" alt="image from video 00:06:33.143" data-time="00:06:33.143"></a><span class="audio-time caption" data-speaker="Sacha" data-start="391.544000" data-stop="393.005000"><strong class="speaker-name">Sacha:</strong> It says Orgsley Revived. What</span> <span class="audio-time caption" data-speaker="Sacha" data-start="393.125000" data-stop="393.765000">is that even?</span> <span class="audio-time caption" data-speaker="Sacha" data-start="394.226000" data-stop="396.516000">But I can press Ctrl+; and</span> <span class="audio-time caption" data-speaker="Sacha" data-start="396.517000" data-stop="398.129000">it uses substitute.el.</span> <p></p><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-06-40-066.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-06-40-066.jpg" alt="image from video 00:06:40.066" data-time="00:06:40.066"></a><span class="audio-time caption" data-speaker="Sacha" data-start="398.729000" data-stop="400.570000"><strong class="speaker-name">Sacha:</strong> Orgzly Revived here. And</span> <span class="audio-time caption" data-speaker="Sacha" data-start="403.032000" data-stop="404.213000">what that does, in addition</span> <span class="audio-time caption" data-speaker="Sacha" data-start="404.273000" data-stop="406.415000">to the substitutions that happen</span> <span class="audio-time caption" data-speaker="Sacha" data-start="406.435000" data-stop="407.515000">throughout the buffer, so for</span> <span class="audio-time caption" data-speaker="Sacha" data-start="407.555000" data-stop="408.416000">example, if I'm adding a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="408.476000" data-stop="408.936000">transcript,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="409.357000" data-stop="410.718000">it will change that all</span> <span class="audio-time caption" data-speaker="Sacha" data-start="410.778000" data-stop="411.578000">throughout the</span> <span class="audio-time caption" data-speaker="Sacha" data-start="412.339000" data-stop="414.080000">transcript. I've also added some</span> <span class="audio-time caption" data-speaker="Sacha" data-start="414.160000" data-stop="415.561000">code because this is Emacs,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="415.601000" data-stop="416.402000">I can just do that.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="416.782000" data-stop="418.443000">that saves this in common</span> <span class="audio-time caption" data-speaker="Sacha" data-start="418.543000" data-stop="421.185000">errors or commonly misrecognized words.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="421.586000" data-stop="423.307000">And then when I recognize text,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="424.148000" data-stop="425.168000">whether it's before a voice</span> <span class="audio-time caption" data-speaker="Sacha" data-start="425.188000" data-stop="425.849000">command or</span> <span class="audio-time caption" data-speaker="Sacha" data-start="426.489000" data-stop="427.350000">if I'm just doing this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="427.410000" data-stop="427.870000">or whatever, it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="428.771000" data-stop="429.712000">will apply all of those</span> <span class="audio-time caption" data-speaker="Sacha" data-start="429.752000" data-stop="430.512000">common fixes.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="431.913000" data-stop="432.874000">Common edits.</span> <p></p><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-07-16-061.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-07-16-061.jpg" alt="image from video 00:07:16.061" data-time="00:07:16.061"></a><span class="audio-time caption" data-speaker="Sacha" data-start="433.314000" data-stop="434.115000"><strong class="speaker-name">Sacha:</strong> Here we go. So it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="434.195000" data-stop="435.416000">automatically adds it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="436.056000" data-stop="438.118000">to this thing here, which</span> <span class="audio-time caption" data-speaker="Sacha" data-start="438.178000" data-stop="439.118000">gets saved</span> <span class="audio-time caption" data-speaker="Sacha" data-start="439.338000" data-stop="440.439000">automatically for me.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="440.939000" data-stop="442.300000">And that's the change</span> <span class="audio-time caption" data-speaker="Sacha" data-start="442.340000" data-stop="444.482000">that I've just added.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="445.323000" data-stop="448.706000"><strong class="speaker-name">Linkarzu:</strong>  Interesting. Pretty cool.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="448.726000" data-stop="449.606000"><strong class="speaker-name">Sacha:</strong>  So it's really nice to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="449.647000" data-stop="452.909000">have that kind of [dictionary] shortcut</span> <span class="audio-time caption" data-speaker="Sacha" data-start="453.890000" data-stop="454.871000">built into the way that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="454.911000" data-stop="455.992000">I edit things. All I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="456.032000" data-stop="457.993000">know is I just hit Ctrl+;</span> <span class="audio-time caption" data-speaker="Sacha" data-start="458.554000" data-stop="459.714000">and put in what it's</span> <span class="audio-time caption" data-speaker="Sacha" data-start="459.734000" data-stop="460.395000">supposed to be.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="460.915000" data-stop="462.156000">And then it gets</span> <span class="audio-time caption" data-speaker="Sacha" data-start="462.157000" data-stop="463.397000">not only fixed in</span> <span class="audio-time caption" data-speaker="Sacha" data-start="463.437000" data-stop="464.818000">my current file, but in</span> <span class="audio-time caption" data-speaker="Sacha" data-start="464.838000" data-stop="465.699000">the future as well.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="466.380000" data-stop="468.962000"><strong class="speaker-name">Linkarzu:</strong>  Yeah, I need to</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="469.002000" data-stop="470.843000">work on my transcript game.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="471.000000">7:51</span> <strong>How the live transcript began</strong></div><p></p><span class="audio-time caption" data-speaker="Sacha" data-start="471.339000" data-stop="474.321000"><strong class="speaker-name">Sacha:</strong>  Like many things in Emacs,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="474.381000" data-stop="476.121000">this came out of a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="476.202000" data-stop="478.383000">bunch of small, seemingly unrelated</span> <span class="audio-time caption" data-speaker="Sacha" data-start="478.423000" data-stop="478.743000">hacks.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="479.383000" data-stop="481.044000">One was, I was having</span> <span class="audio-time caption" data-speaker="Sacha" data-start="481.064000" data-stop="483.445000">this conversation with a French</span> <span class="audio-time caption" data-speaker="Sacha" data-start="483.505000" data-stop="484.406000">tutor and I was like,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="484.906000" data-stop="486.027000">man, I am having a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="486.067000" data-stop="487.067000">hard time making sure... Like,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="487.507000" data-stop="488.568000">he would switch into lecture</span> <span class="audio-time caption" data-speaker="Sacha" data-start="488.588000" data-stop="489.328000">mode and I was trying</span> <span class="audio-time caption" data-speaker="Sacha" data-start="489.348000" data-stop="490.629000">to have more conversation mode</span> <span class="audio-time caption" data-speaker="Sacha" data-start="490.669000" data-stop="493.577000">so I could practice some more, right?</span> <span class="audio-time caption" data-speaker="Sacha" data-start="493.578000" data-stop="496.472000">So I think I've got this right.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="496.896000" data-stop="498.437000">Anyway, so I had</span> <span class="audio-time caption" data-speaker="Sacha" data-start="499.317000" data-stop="500.918000">a large language model generate</span> <span class="audio-time caption" data-speaker="Sacha" data-start="501.038000" data-stop="502.959000">a Python server, because sometimes</span> <span class="audio-time caption" data-speaker="Sacha" data-start="502.979000" data-stop="505.139000">I'm like, I just want</span> <span class="audio-time caption" data-speaker="Sacha" data-start="505.179000" data-stop="506.540000">a proof of concept for this.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="506.920000" data-stop="508.401000">I don't necessarily know all</span> <span class="audio-time caption" data-speaker="Sacha" data-start="508.421000" data-stop="509.421000">the details of how to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="509.461000" data-stop="510.742000">work with the Silero voice</span> <span class="audio-time caption" data-speaker="Sacha" data-start="510.802000" data-stop="511.856000">activity detection thing.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="511.857000" data-stop="513.423000">Anyway, so make me a server that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="513.443000" data-stop="514.423000">can listen to my audio</span> <span class="audio-time caption" data-speaker="Sacha" data-start="514.463000" data-stop="515.744000">devices in real time without</span> <span class="audio-time caption" data-speaker="Sacha" data-start="515.784000" data-stop="518.125000">messing up my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="519.085000" data-stop="520.366000">actual microphone use if I'm</span> <span class="audio-time caption" data-speaker="Sacha" data-start="520.426000" data-stop="520.826000">on stream.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="521.266000" data-stop="521.966000">Okay, so I have this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="522.006000" data-stop="523.207000">Python server and then I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="523.247000" data-stop="524.227000">wanted to visualize it within</span> <span class="audio-time caption" data-speaker="Sacha" data-start="524.267000" data-stop="525.187000">Emacs. I got</span> <span class="audio-time caption" data-speaker="Sacha" data-start="525.227000" data-stop="527.008000">that visualizing inside Emacs.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="527.048000" data-stop="527.908000">Then I wanted to say,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="528.648000" data-stop="530.369000">all right, you know,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="530.849000" data-stop="531.609000">why don't I have speech</span> <span class="audio-time caption" data-speaker="Sacha" data-start="531.649000" data-stop="533.130000">recognition listening on this thing</span> <span class="audio-time caption" data-speaker="Sacha" data-start="533.170000" data-stop="533.930000">so that if I say</span> <span class="audio-time caption" data-speaker="Sacha" data-start="534.010000" data-stop="538.420000">something, it can check it off,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="538.421000" data-stop="539.818000">like playing bingo, you know,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="539.819000" data-stop="540.852000">if I say it, then it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="540.892000" data-stop="541.813000">gives me a note that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="541.833000" data-stop="542.833000">I've actually said that word</span> <span class="audio-time caption" data-speaker="Sacha" data-start="543.153000" data-stop="544.073000">or phrase. And so</span> <span class="audio-time caption" data-speaker="Sacha" data-start="544.734000" data-stop="545.274000">it could show me a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="545.314000" data-stop="546.134000">list of the words or</span> <span class="audio-time caption" data-speaker="Sacha" data-start="546.154000" data-stop="547.214000">phrases that I still want</span> <span class="audio-time caption" data-speaker="Sacha" data-start="547.255000" data-stop="548.355000">to say in conversation.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="548.915000" data-stop="549.336000">And then, as</span> <span class="audio-time caption" data-speaker="Sacha" data-start="550.317000" data-stop="551.719000">we're preparing for this conversation,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="551.739000" data-stop="552.620000">I was like, you know</span> <span class="audio-time caption" data-speaker="Sacha" data-start="552.640000" data-stop="553.622000">what would be really cool</span> <span class="audio-time caption" data-speaker="Sacha" data-start="554.122000" data-stop="555.204000">is if I could</span> <span class="audio-time caption" data-speaker="Sacha" data-start="555.605000" data-stop="556.841000">have that speech recognition,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="556.842000" data-stop="558.469000">which is running anyway,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="558.929000" data-stop="560.171000">give me a transcript of</span> <span class="audio-time caption" data-speaker="Sacha" data-start="560.231000" data-stop="561.673000">the current conversation in a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="562.174000" data-stop="563.808000">way that I can use it.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="563.809000" data-stop="565.017000">I could then clip</span> <span class="audio-time caption" data-speaker="Sacha" data-start="565.137000" data-stop="565.998000">if I wanted to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="566.479000" data-stop="567.760000">because sometimes I'll be</span> <span class="audio-time caption" data-speaker="Sacha" data-start="567.880000" data-stop="570.302000">doing a live stream and</span> <span class="audio-time caption" data-speaker="Sacha" data-start="570.342000" data-stop="570.723000">I'd be like,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="571.243000" data-stop="572.044000">you know, that was a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="572.084000" data-stop="573.125000">really cool demo and I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="573.165000" data-stop="574.326000">should grab that. So that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="574.446000" data-stop="575.347000">I can just grab the</span> <span class="audio-time caption" data-speaker="Sacha" data-start="575.367000" data-stop="576.428000">screenshot or</span> <span class="audio-time caption" data-speaker="Sacha" data-start="577.269000" data-stop="578.150000">put it into</span> <span class="audio-time caption" data-speaker="Sacha" data-start="579.051000" data-stop="579.831000">a blog post or just</span> <span class="audio-time caption" data-speaker="Sacha" data-start="579.851000" data-stop="581.633000">get a text or extract</span> <span class="audio-time caption" data-speaker="Sacha" data-start="582.073000" data-stop="582.854000">a clip out of it,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="582.914000" data-stop="584.316000">turn it into a video, right?</span> <span class="audio-time caption" data-speaker="Sacha" data-start="585.957000" data-stop="586.997000">And so now I have</span> <span class="audio-time caption" data-speaker="Sacha" data-start="587.037000" data-stop="588.318000">this thing where,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="588.859000" data-stop="589.679000">hang on a second,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="590.139000" data-stop="591.821000">if I'm somewhere else, for</span> <span class="audio-time caption" data-speaker="Sacha" data-start="591.881000" data-stop="592.321000">example,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="593.081000" data-stop="593.822000">it's easy for me to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="593.862000" data-stop="593.885000">get to.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="593.000000">9:53</span> <strong>Prepare to fast forward</strong></div><p></p><span class="audio-time caption" data-speaker="Sacha" data-start="593.886000" data-stop="594.622000"><strong class="speaker-name">Sacha:</strong> I have a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="594.662000" data-stop="595.503000">voice command for it.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="596.023000" data-stop="597.284000">"Prepare to fast forward",</span> <span class="audio-time caption" data-speaker="Sacha" data-start="598.765000" data-stop="599.525000">which is of course a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="599.586000" data-stop="600.592000">Spaceballs reference.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="600.593000" data-stop="601.427000">It'll show me then</span> <span class="audio-time caption" data-speaker="Sacha" data-start="601.867000" data-stop="602.547000">this thing.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="603.428000" data-stop="607.030000">And if we are... Let me load this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="607.451000" data-stop="609.172000">real quick.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="610.042000" data-stop="611.083000">It will show me</span> <span class="audio-time caption" data-speaker="Sacha" data-start="611.143000" data-stop="612.745000">the video that we have</span> <span class="audio-time caption" data-speaker="Sacha" data-start="613.125000" data-stop="614.166000">or at least the recording</span> <span class="audio-time caption" data-speaker="Sacha" data-start="614.186000" data-stop="615.187000">that I've got of it.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="615.908000" data-stop="617.089000">That's on the right side.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="617.109000" data-stop="618.511000">Here, this MPV is controlled</span> <span class="audio-time caption" data-speaker="Sacha" data-start="618.531000" data-stop="619.291000">by Emacs.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="620.032000" data-stop="623.540000">If I go here...</span> <span class="audio-time caption" data-speaker="Sacha" data-start="624.180000" data-stop="625.690000">Let's say this one.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="625.691000" data-stop="627.163000">So here's your waveform for it.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="627.183000" data-stop="628.457000">If I play it back:</span> <span class="audio-time caption" data-speaker="Sacha" data-start="628.458000" data-stop="629.665000">"which is of course</span> <span class="audio-time caption" data-speaker="Sacha" data-start="629.705000" data-stop="630.904000">a Spaceballs reference",</span> <span class="audio-time caption" data-speaker="Sacha" data-start="630.905000" data-stop="632.848000">it'll show me then this thing.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="633.608000" data-stop="636.291000">And I can copy the text if I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="636.331000" data-stop="637.291000">wanted to, or I can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="637.732000" data-stop="638.553000">extract this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="639.333000" data-stop="640.875000">into its own video.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="641.000000">10:41</span> <strong>Talking with Emacs
</strong></div><p></p><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-00-00-000.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-00-00-000.jpg" alt="image from video 00:00:00.000" data-time="00:00:00.000"></a><span class="audio-time caption" data-speaker="Linkarzu" data-start="641.222000" data-stop="642.383000"><strong class="speaker-name">Linkarzu:</strong>  What other tricks do you</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="642.423000" data-stop="643.504000">want to show us there?</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="643.644000" data-stop="644.785000">What other things do you</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="644.805000" data-stop="646.287000">want to share about,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="647.088000" data-stop="649.390000">brag about your Emacs workflow?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="650.001000" data-stop="650.801000"><strong class="speaker-name">Sacha:</strong>  It's really hard to say.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="653.447000" data-stop="654.388000">I told you how M-x doctor</span> <span class="audio-time caption" data-speaker="Sacha" data-start="654.408000" data-stop="655.629000">was the beginning of my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="656.449000" data-stop="657.230000">experience, right?</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="657.231000" data-stop="657.970000"><strong class="speaker-name">Linkarzu:</strong>  Your journey, yep.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="658.270000" data-stop="659.371000"><strong class="speaker-name">Sacha:</strong>  Yes, yes. So this is</span> <span class="audio-time caption" data-speaker="Sacha" data-start="659.431000" data-stop="660.252000">the doctor. You can say</span> <span class="audio-time caption" data-speaker="Sacha" data-start="660.312000" data-stop="660.912000">things like,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="661.953000" data-stop="664.215000">"I'm feeling happy today," as</span> <span class="audio-time caption" data-speaker="Sacha" data-start="664.295000" data-stop="665.115000">I said, right?</span> <span class="audio-time caption" data-speaker="Sacha" data-start="665.896000" data-stop="666.917000">And then it uses that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="666.977000" data-stop="668.578000">to generate its response.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="669.598000" data-stop="671.220000">But I have this conversation mode.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="673.621000" data-stop="674.782000">"Yes, especially when I get</span> <span class="audio-time caption" data-speaker="Sacha" data-start="674.822000" data-stop="675.783000">to tinker with Emacs."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="679.095000" data-stop="681.340000">And then, uh... "What happened</span> <span class="audio-time caption" data-speaker="Sacha" data-start="681.380000" data-stop="682.162000">when you get to tinker</span> <span class="audio-time caption" data-speaker="Sacha" data-start="682.182000" data-stop="682.743000">with Emacs?"</span> <span class="audio-time caption" data-speaker="Sacha" data-start="683.550000" data-stop="685.450000">Did that...? Does the TTS come</span> <span class="audio-time caption" data-speaker="Sacha" data-start="685.470000" data-stop="686.731000">through? Yeah, it does.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="687.911000" data-stop="688.591000">And the reason I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="689.391000" data-stop="690.652000">did this was because after</span> <span class="audio-time caption" data-speaker="Sacha" data-start="690.692000" data-stop="691.572000">I showed off my vocal</span> <span class="audio-time caption" data-speaker="Sacha" data-start="691.592000" data-stop="693.152000">commands to Emacs Paris, they</span> <span class="audio-time caption" data-speaker="Sacha" data-start="693.172000" data-stop="694.233000">were like, oh, you know,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="694.313000" data-stop="694.873000">what if you can have</span> <span class="audio-time caption" data-speaker="Sacha" data-start="694.893000" data-stop="695.613000">a conversation? So</span> <span class="audio-time caption" data-speaker="Sacha" data-start="696.113000" data-stop="696.813000">I was like, OK, let's</span> <span class="audio-time caption" data-speaker="Sacha" data-start="697.173000" data-stop="698.253000">come back full circle to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="698.293000" data-stop="699.534000">the thing that got me</span> <span class="audio-time caption" data-speaker="Sacha" data-start="699.574000" data-stop="700.734000">curious about Emacs in the</span> <span class="audio-time caption" data-speaker="Sacha" data-start="700.754000" data-stop="702.554000">first place, and</span> <span class="audio-time caption" data-speaker="Sacha" data-start="703.015000" data-stop="703.895000">voice enable that.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="704.275000" data-stop="705.175000">And then, of course, this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="705.235000" data-stop="706.255000">is a very old AI.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="706.295000" data-stop="707.216000">It's much more interesting if</span> <span class="audio-time caption" data-speaker="Sacha" data-start="707.256000" data-stop="708.256000">you apply it to the</span> <span class="audio-time caption" data-speaker="Sacha" data-start="708.356000" data-stop="709.096000">new models.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="709.676000" data-stop="711.857000">So if I want to, for example,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="712.277000" data-stop="712.957000">practice French.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="714.217000" data-stop="715.657000">This is one of</span> <span class="audio-time caption" data-speaker="Sacha" data-start="716.858000" data-stop="718.558000">my voice commands here.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="719.558000" data-stop="720.211000">Then I can...</span> <p></p><span class="audio-time caption" data-speaker="Doctor" data-start="720.212000" data-stop="720.679000"><strong class="speaker-name">Doctor:</strong>  "What would</span> <span class="audio-time caption" data-speaker="Doctor" data-start="720.739000" data-stop="721.919000">happen? I would</span> <span class="audio-time caption" data-speaker="Doctor" data-start="721.959000" data-stop="722.799000">appreciate it if you would</span> <span class="audio-time caption" data-speaker="Doctor" data-start="722.839000" data-stop="723.319000">continue."</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="724.259000" data-stop="724.919000"><strong class="speaker-name">Sacha:</strong>  Okay, here we are.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="725.560000" data-stop="726.380000">Then I might say,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="728.340000" data-stop="730.161000">"Je parle avec linkarzu."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="730.721000" data-stop="732.022000">And then I think...</span> <span class="audio-time caption" data-speaker="Sacha" data-start="733.322000" data-stop="733.942000">There you go. It</span> <span class="audio-time caption" data-speaker="Sacha" data-start="734.740000" data-stop="735.741000">will eventually...</span> <span class="audio-time caption" data-speaker="Sacha" data-start="736.292000" data-stop="736.632000">Maybe? Thank you.</span> <p></p><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-12-18-696.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-12-18-696.jpg" alt="image from video 00:12:18.696" data-time="00:12:18.696"></a><span class="audio-time caption" data-speaker="Gemini" data-start="738.688000" data-stop="740.750000"><strong class="speaker-name">Gemini:</strong>  "Ah, c'est super de discuter</span> <span class="audio-time caption" data-speaker="Gemini" data-start="740.790000" data-stop="742.652000">avec lui ! C'est toujours enrichissant</span> <span class="audio-time caption" data-speaker="Gemini" data-start="742.672000" data-stop="744.514000">de partager (to share) ses passions</span> <span class="audio-time caption" data-speaker="Gemini" data-start="744.574000" data-stop="746.092000">en direct. Quel sujet</span> <span class="audio-time caption" data-speaker="Gemini" data-start="746.093000" data-stop="747.757000">abordez-vous ensemble aujourd'hui?"</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="748.307000" data-stop="749.708000"><strong class="speaker-name">Sacha:</strong>  Alright, so</span> <span class="audio-time caption" data-speaker="Sacha" data-start="750.808000" data-stop="752.249000">this now uses a large</span> <span class="audio-time caption" data-speaker="Sacha" data-start="752.309000" data-stop="753.769000">language model. This one uses</span> <span class="audio-time caption" data-speaker="Sacha" data-start="754.310000" data-stop="756.791000">Gemini because gptel makes it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="756.851000" data-stop="757.851000">super easy to plug in</span> <span class="audio-time caption" data-speaker="Sacha" data-start="757.871000" data-stop="758.551000">different models.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="759.752000" data-stop="760.572000">So if I wanted</span> <span class="audio-time caption" data-speaker="Sacha" data-start="760.612000" data-stop="761.533000">to practice</span> <span class="audio-time caption" data-speaker="Sacha" data-start="762.013000" data-stop="763.394000">French, but I don't necessarily</span> <span class="audio-time caption" data-speaker="Sacha" data-start="763.414000" data-stop="764.254000">want to bug people</span> <span class="audio-time caption" data-speaker="Sacha" data-start="765.675000" data-stop="766.695000">because I take a while</span> <span class="audio-time caption" data-speaker="Sacha" data-start="766.735000" data-stop="767.695000">to think about my words</span> <span class="audio-time caption" data-speaker="Sacha" data-start="767.775000" data-stop="768.976000">and sometimes I'm looking stuff up,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="769.396000" data-stop="770.076000">then at least I can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="770.116000" data-stop="771.497000">simulate that kind of conversation.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="771.557000" data-stop="772.417000">And there are a gazillion</span> <span class="audio-time caption" data-speaker="Sacha" data-start="772.558000" data-stop="773.857000">apps that will sell you</span> <span class="audio-time caption" data-speaker="Sacha" data-start="773.858000" data-stop="775.179000">a subscription to do this.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="775.939000" data-stop="777.220000">But because it's here and</span> <span class="audio-time caption" data-speaker="Sacha" data-start="777.280000" data-stop="778.400000">it's plain text and I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="778.460000" data-stop="779.441000">can control it, then I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="779.461000" data-stop="781.181000">can more easily go through it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="781.261000" data-stop="784.883000">afterwards and analyze the vocabulary</span> <span class="audio-time caption" data-speaker="Sacha" data-start="785.163000" data-stop="787.784000">or correct the things</span> <span class="audio-time caption" data-speaker="Sacha" data-start="787.824000" data-stop="788.945000">that it didn't get right,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="789.085000" data-stop="790.245000">which I know is because</span> <span class="audio-time caption" data-speaker="Sacha" data-start="790.345000" data-stop="792.086000">of the speech recognition and</span> <span class="audio-time caption" data-speaker="Sacha" data-start="792.126000" data-stop="792.927000">not because of me or</span> <span class="audio-time caption" data-speaker="Sacha" data-start="792.947000" data-stop="793.507000">whatever, right?</span> <span class="audio-time caption" data-speaker="Sacha" data-start="794.787000" data-stop="795.648000">So it's so much fun</span> <span class="audio-time caption" data-speaker="Sacha" data-start="795.688000" data-stop="796.668000">to be able to tinker</span> <span class="audio-time caption" data-speaker="Sacha" data-start="796.748000" data-stop="798.229000">with Emacs to do what</span> <span class="audio-time caption" data-speaker="Sacha" data-start="798.329000" data-stop="799.169000">I wanted to do.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="799.000000">13:19</span> <strong>Life in tiny time chunks</strong></div><p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="799.596000" data-stop="800.857000"><strong class="speaker-name">Linkarzu:</strong>  How long do you spend</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="800.897000" data-stop="801.877000">sitting? Like do you spend</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="801.897000" data-stop="803.058000">sitting most of the day?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="804.327000" data-stop="805.448000"><strong class="speaker-name">Sacha:</strong>  Most of the time, actually,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="805.468000" data-stop="806.149000">I have to go help</span> <span class="audio-time caption" data-speaker="Sacha" data-start="806.209000" data-stop="807.370000">out the kiddo, right?</span> <span class="audio-time caption" data-speaker="Sacha" data-start="808.331000" data-stop="809.672000">So that keeps me away</span> <span class="audio-time caption" data-speaker="Sacha" data-start="809.712000" data-stop="810.753000">from the computer, which is</span> <span class="audio-time caption" data-speaker="Sacha" data-start="810.853000" data-stop="811.894000">one of the reasons why</span> <span class="audio-time caption" data-speaker="Sacha" data-start="812.415000" data-stop="813.736000">I've had to learn how</span> <span class="audio-time caption" data-speaker="Sacha" data-start="813.776000" data-stop="815.938000">to deal with very fragmented time.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="816.799000" data-stop="817.980000">Not much time sitting in</span> <span class="audio-time caption" data-speaker="Sacha" data-start="818.000000" data-stop="818.881000">front of the computer to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="818.921000" data-stop="820.422000">focus on something. Everything that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="820.562000" data-stop="821.183000">I want to do</span> <span class="audio-time caption" data-speaker="Sacha" data-start="821.583000" data-stop="822.764000">has to be something that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="822.784000" data-stop="823.265000">I can do in 5,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="823.325000" data-stop="824.286000">10, 15 minutes, maybe 30</span> <span class="audio-time caption" data-speaker="Sacha" data-start="824.326000" data-stop="824.966000">minutes.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="826.267000" data-stop="826.747000">There might be...</span> <span class="audio-time caption" data-speaker="Sacha" data-start="827.208000" data-stop="828.048000">At the moment,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="828.088000" data-stop="830.850000">she is fairly independent, but I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="830.890000" data-stop="831.970000">can still be interrupted by</span> <span class="audio-time caption" data-speaker="Sacha" data-start="832.030000" data-stop="833.671000">somebody who needs breakfast or</span> <span class="audio-time caption" data-speaker="Sacha" data-start="833.711000" data-stop="834.772000">somebody who needs a hug</span> <span class="audio-time caption" data-speaker="Sacha" data-start="834.832000" data-stop="836.573000">and whatever. So I need to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="836.613000" data-stop="837.573000">be able to capture a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="837.613000" data-stop="838.434000">thought, get it out of</span> <span class="audio-time caption" data-speaker="Sacha" data-start="838.474000" data-stop="839.214000">the way and work on</span> <span class="audio-time caption" data-speaker="Sacha" data-start="839.234000" data-stop="840.195000">things in tiny chunks.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="841.261000" data-stop="841.702000"><strong class="speaker-name">Linkarzu:</strong>  I've seen</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="842.122000" data-stop="843.944000">my wife, how she handles</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="844.004000" data-stop="845.045000">things. And I'm like, man,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="845.865000" data-stop="846.266000">it's a lot of</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="847.226000" data-stop="848.868000">mental overhead, you know?</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="849.858000" data-stop="852.179000">That's a whole other</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="852.219000" data-stop="853.521000">conversation to say, see,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="854.141000" data-stop="855.482000">men are the ones that</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="855.582000" data-stop="857.944000">make progress, build their professional</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="858.004000" data-stop="859.546000">careers and wives, you know,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="859.606000" data-stop="860.707000">usually take care of the kids.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="861.347000" data-stop="865.200000">It's a huge sacrifice.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="865.201000" data-stop="866.291000">What do you think?</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="866.325000" data-stop="866.956000">How do you feel about</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="866.976000" data-stop="867.257000">all that?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="868.183000" data-stop="869.184000"><strong class="speaker-name">Sacha:</strong>  I signed up for it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="869.306000" data-stop="870.908000">totally knowing what I was</span> <span class="audio-time caption" data-speaker="Sacha" data-start="870.948000" data-stop="872.210000">getting into. I was like,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="872.267000" data-stop="873.447000">okay, there's a lot involved</span> <span class="audio-time caption" data-speaker="Sacha" data-start="873.487000" data-stop="875.168000">in being the default parent,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="875.228000" data-stop="876.008000">right? The primary</span> <span class="audio-time caption" data-speaker="Sacha" data-start="876.788000" data-stop="878.549000">person who patches up.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="879.529000" data-stop="880.329000">But I think</span> <span class="audio-time caption" data-speaker="Sacha" data-start="880.850000" data-stop="882.150000">partly because of Org Mode</span> <span class="audio-time caption" data-speaker="Sacha" data-start="882.190000" data-stop="882.730000">and Emacs,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="883.013000" data-stop="884.454000">it's been very, very helpful</span> <span class="audio-time caption" data-speaker="Sacha" data-start="884.594000" data-stop="885.554000">to me to be able</span> <span class="audio-time caption" data-speaker="Sacha" data-start="885.574000" data-stop="885.874000">to say,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="886.254000" data-stop="887.375000">"Yeah, that's a great idea.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="887.415000" data-stop="888.135000">I'm going to write that</span> <span class="audio-time caption" data-speaker="Sacha" data-start="888.175000" data-stop="889.355000">down in my phone or</span> <span class="audio-time caption" data-speaker="Sacha" data-start="889.395000" data-stop="890.456000">on my computer" - usually on</span> <span class="audio-time caption" data-speaker="Sacha" data-start="890.476000" data-stop="891.196000">my phone back in the early</span> <span class="audio-time caption" data-speaker="Sacha" data-start="891.536000" data-stop="891.976000">parenting days.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="894.217000" data-stop="895.098000">I'm away from my computer.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="895.118000" data-stop="896.779000">I'm at the playground.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="897.300000" data-stop="898.220000">So the fact that I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="898.260000" data-stop="898.981000">can capture</span> <span class="audio-time caption" data-speaker="Sacha" data-start="899.561000" data-stop="900.903000">a note into Org Mode</span> <span class="audio-time caption" data-speaker="Sacha" data-start="901.283000" data-stop="902.544000">and then know that I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="902.604000" data-stop="903.525000">can get back to it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="903.645000" data-stop="905.186000">in several years when I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="905.226000" data-stop="906.027000">have more free time,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="906.427000" data-stop="907.308000">or that Org Mode can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="907.348000" data-stop="908.569000">keep track of the deadlines</span> <span class="audio-time caption" data-speaker="Sacha" data-start="908.649000" data-stop="909.590000">that might be a year</span> <span class="audio-time caption" data-speaker="Sacha" data-start="909.630000" data-stop="910.430000">from now, like when I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="910.490000" data-stop="910.731000">need to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="911.111000" data-stop="912.032000">renew my passport,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="912.452000" data-stop="913.933000">and just have that surface</span> <span class="audio-time caption" data-speaker="Sacha" data-start="913.953000" data-stop="914.874000">the reminder, at the right</span> <span class="audio-time caption" data-speaker="Sacha" data-start="914.914000" data-stop="916.195000">time with the notes on</span> <span class="audio-time caption" data-speaker="Sacha" data-start="916.255000" data-stop="917.436000">exactly what I need to do,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="917.936000" data-stop="918.857000">and the links to wherever</span> <span class="audio-time caption" data-speaker="Sacha" data-start="918.877000" data-stop="919.518000">I need to go, I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="920.058000" data-stop="921.259000">think that has been very,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="921.299000" data-stop="922.160000">very helpful for me.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="923.000000">15:23</span> <strong>Capturing tasks on phone and computer</strong></div><p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="923.000000" data-stop="924.641000"><strong class="speaker-name">Linkarzu:</strong>  Good point that you brought</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="924.721000" data-stop="925.961000">up because I don't know,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="926.381000" data-stop="927.241000">quite a few days ago</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="927.281000" data-stop="928.381000">I just came up, well,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="928.742000" data-stop="930.642000">the clanker came up with a</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="931.022000" data-stop="932.202000">method, we could say,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="932.563000" data-stop="934.003000">of me just tapping it,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="934.263000" data-stop="935.323000">saying something to it,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="935.783000" data-stop="936.543000">and when I come to</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="936.563000" data-stop="938.284000">my computer, the task is</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="938.364000" data-stop="939.404000">there in Neovim,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="939.784000" data-stop="941.645000">right? Yes. Because I forget,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="941.925000" data-stop="943.865000">I'm outside, I'm like, okay,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="943.945000" data-stop="944.946000">I have to remember this,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="944.966000" data-stop="945.886000">I have to, I'm not</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="945.906000" data-stop="946.926000">gonna remember it, it's just</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="946.966000" data-stop="948.126000">gonna go away. So I</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="948.146000" data-stop="948.827000">do it on the phone,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="949.287000" data-stop="950.167000">shows up in Neovim.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="951.767000" data-stop="953.176000">It's a huge benefit.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="953.177000" data-stop="953.908000">How do you do it</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="955.989000" data-stop="957.269000"><strong class="speaker-name">Sacha:</strong>  For my phone, I often</span> <span class="audio-time caption" data-speaker="Sacha" data-start="957.330000" data-stop="958.650000">just put it into</span> <span class="audio-time caption" data-speaker="Sacha" data-start="958.670000" data-stop="961.131000">Orgzly Revived, sometimes with dictation,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="961.191000" data-stop="962.291000">sometimes just typing in a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="962.331000" data-stop="963.252000">few keywords to help me</span> <span class="audio-time caption" data-speaker="Sacha" data-start="963.292000" data-stop="965.112000">remember. A lot of times,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="965.132000" data-stop="966.233000">if I'm coming across...</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="966.453000" data-stop="968.614000"><strong class="speaker-name">Linkarzu:</strong>  Orgzly Revived, what is it?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="969.074000" data-stop="972.519000"><strong class="speaker-name">Sacha:</strong>  (Hah, I clearly can't spell) ogrzly?</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="972.520000" data-stop="973.586000"><strong class="speaker-name">Linkarzu:</strong>  If you want to show it,</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="973.587000" data-stop="974.416000">I don't know.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="975.576000" data-stop="976.497000"><strong class="speaker-name">Sacha:</strong>  It's on my phone.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="977.310000" data-stop="978.531000">I'm gonna say, okay, because</span> <span class="audio-time caption" data-speaker="Sacha" data-start="978.551000" data-stop="979.522000">this is what, Orgzly.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="979.523000" data-stop="982.373000"><strong class="speaker-name">Linkarzu:</strong>  Is it like an app?</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="982.413000" data-stop="983.493000"><strong class="speaker-name">Sacha:</strong>  Yeah, so I use an</span> <span class="audio-time caption" data-speaker="Sacha" data-start="983.593000" data-stop="984.974000">Android phone. And now there</span> <span class="audio-time caption" data-speaker="Sacha" data-start="984.994000" data-stop="986.375000">are actually a gazillion apps</span> <span class="audio-time caption" data-speaker="Sacha" data-start="986.535000" data-stop="989.417000">that talk Org Mode and can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="989.437000" data-stop="990.557000">synchronize with your files and</span> <span class="audio-time caption" data-speaker="Sacha" data-start="990.577000" data-stop="990.977000">whatever.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="991.438000" data-stop="992.918000">I'm currently still just using</span> <span class="audio-time caption" data-speaker="Sacha" data-start="992.979000" data-stop="994.699000">Orgzly Revived as an app</span> <span class="audio-time caption" data-speaker="Sacha" data-start="994.799000" data-stop="996.360000">on my phone with Syncthing,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="996.801000" data-stop="998.021000">With Syncthing being the</span> <span class="audio-time caption" data-speaker="Sacha" data-start="998.501000" data-stop="1000.643000">free synchronization that I can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1000.783000" data-stop="1002.103000">use to just get my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1002.123000" data-stop="1002.904000">files back and forth.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1004.267000" data-stop="1005.327000">But there are lots of</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1005.367000" data-stop="1006.488000">other alternatives, so you can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1006.528000" data-stop="1007.728000">find something that fits you well.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1008.228000" data-stop="1010.069000">I am very tempted to figure out</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1010.449000" data-stop="1012.269000">finally how to use Emacs</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1012.349000" data-stop="1014.270000">native, because there's an Emacs</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1014.330000" data-stop="1015.250000">port for Android.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1016.330000" data-stop="1017.351000">Especially if I can get</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1017.451000" data-stop="1019.191000">my speech synthesis and speech</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1019.231000" data-stop="1020.351000">recognition working on it. I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1020.371000" data-stop="1021.432000">think that would be amazing</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1021.472000" data-stop="1022.032000">to have this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1022.412000" data-stop="1024.092000">kind of conversation with</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1024.793000" data-stop="1025.593000">Emacs along the way.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="1025.000000">17:05</span> <strong>Capturing tasks by voice</strong></div><p></p><span class="audio-time caption" data-speaker="Sacha" data-start="1025.993000" data-stop="1026.913000"><strong class="speaker-name">Sacha:</strong> And then on the computer,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1026.973000" data-stop="1027.793000">if I need to capture</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1027.814000" data-stop="1028.954000">something quickly, oh, I can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1028.994000" data-stop="1031.675000">show you this one. Let's see. Okay.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1033.555000" data-stop="1034.936000">"Remind me to put together</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1034.956000" data-stop="1036.917000">a quick description of my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1038.718000" data-stop="1039.559000">to-do workflow."</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1041.545000" data-stop="1043.566000">And it will take that note</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1044.007000" data-stop="1044.887000">and it will stick it</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1045.047000" data-stop="1046.848000">into an inbox.</span> <p></p><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-17-36-180.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-17-36-180.jpg" alt="image from video 00:17:36.180" data-time="00:17:36.180"></a><span class="audio-time caption" data-speaker="Sacha" data-start="1047.529000" data-stop="1050.030000"><strong class="speaker-name">Sacha:</strong> Here I'm in live mode</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1050.751000" data-stop="1055.154000">so my public tasks don't get mixed in</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1055.194000" data-stop="1056.150000">with my private tasks.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1056.151000" data-stop="1056.874000">If I do an agenda,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1056.875000" data-stop="1057.835000">it should just be limited to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1057.855000" data-stop="1058.476000">the public stuff.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1059.196000" data-stop="1060.657000">Anyway, so the remind me</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1060.953000" data-stop="1061.893000">created the note for me.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1062.093000" data-stop="1064.194000">It includes the audio</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1064.735000" data-stop="1067.091000">in case the speech recognition</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1067.092000" data-stop="1068.837000">didn't get anything right.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1069.217000" data-stop="1070.437000">And it includes a screenshot</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1070.537000" data-stop="1071.558000">so that in case I'm</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1071.598000" data-stop="1073.241000">doing this from a different application</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1073.242000" data-stop="1075.120000">because my keyboard shortcut is global,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1075.580000" data-stop="1076.280000">I can still see the</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1076.340000" data-stop="1077.761000">context and</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1077.801000" data-stop="1079.142000">find that sort of stuff.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1079.843000" data-stop="1080.764000">So yeah.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="1081.000000">18:01</span> <strong>Building workflows out of little bits of time</strong></div><p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="1081.304000" data-stop="1082.185000"><strong class="speaker-name">Linkarzu:</strong>  Quite interesting.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="1082.565000" data-stop="1084.467000">Quite advanced. Quite complex.</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="1084.847000" data-stop="1085.808000">It must have taken you</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="1086.068000" data-stop="1088.110000">months, years to set all</span> <span class="audio-time caption" data-speaker="Linkarzu" data-start="1088.130000" data-stop="1088.570000">of this up.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="1089.371000" data-stop="1090.012000"><strong class="speaker-name">Sacha:</strong>  As I said, I can</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1090.052000" data-stop="1090.892000">only work in thirty</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1090.912000" data-stop="1091.813000">minutes or so. So this</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1091.933000" data-stop="1093.294000">one, I think was a</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1094.042000" data-stop="1095.263000">10, 15 minute hack.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1096.032000" data-stop="1099.515000">Okay. Little bits of time.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1099.896000" data-stop="1101.297000">This link here to whatever</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1101.317000" data-stop="1102.118000">I was looking at, you</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1102.138000" data-stop="1103.078000">got that free from Org</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1103.098000" data-stop="1105.120000">Mode. This thing here with</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1105.220000" data-stop="1105.921000">a recording,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1106.542000" data-stop="1108.103000">I got that because I</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1108.303000" data-stop="1109.384000">already wanted to save my</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1109.424000" data-stop="1110.565000">recordings anyway, so it was</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1110.605000" data-stop="1111.586000">just a matter of adding</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1111.626000" data-stop="1112.607000">a link to the file</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1112.647000" data-stop="1113.388000">name that I already had</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1113.448000" data-stop="1113.748000">saved.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1114.309000" data-stop="1116.170000">This screenshot, you know, another</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1116.210000" data-stop="1116.751000">five minutes,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1117.111000" data-stop="1118.432000">because I already had Spectacle</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1119.053000" data-stop="1120.494000">capturing the screenshot of both</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1120.534000" data-stop="1120.995000">screens.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1122.138000" data-stop="1123.441000">So it's fairly simple.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1124.102000" data-stop="1125.044000">It will even work for</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1125.084000" data-stop="1125.585000">things like,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1126.086000" data-stop="1127.088000">"remind me next month to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1127.108000" data-stop="1128.151000">follow up with linkarzu to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1128.191000" data-stop="1129.173000">see whether he's switched to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1129.253000" data-stop="1129.573000">Emacs."</span> <p></p><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-18-50-296.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt-00-18-50-296.jpg" alt="image from video 00:18:50.296" data-time="00:18:50.296"></a><span class="audio-time caption" data-speaker="Sacha" data-start="1129.588000" data-stop="1130.233000"><strong class="speaker-name">Sacha:</strong> So this one here, for</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1130.273000" data-stop="1131.575000">example, it says, remind me</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1131.975000" data-stop="1132.876000">next month, et cetera, et</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1132.916000" data-stop="1133.937000">cetera. And it has scheduled</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1133.977000" data-stop="1134.738000">it for next month.</span> <p></p><span class="audio-time caption" data-speaker="Linkarzu" data-start="1135.279000" data-stop="1136.060000"><strong class="speaker-name">Linkarzu:</strong>  That's pretty cool.</span> <p></p><span class="audio-time caption" data-speaker="Sacha" data-start="1137.649000" data-stop="1138.670000"><strong class="speaker-name">Sacha:</strong>  I'm not actually going to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1138.710000" data-stop="1139.570000">pressure you to switch the</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1139.610000" data-stop="1140.971000">Emacs. You can keep using</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1140.991000" data-stop="1142.252000">Neovim and just borrow all</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1142.272000" data-stop="1143.193000">the interesting ideas.</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1143.194000" data-stop="1145.154000">org.nvim is there,</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1145.594000" data-stop="1146.715000">I can adapt it to</span> <span class="audio-time caption" data-speaker="Sacha" data-start="1146.815000" data-stop="1147.715000">do this, you know?</span></div>
<p></p>


</details>

<p>
<a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/Xk02luunlfc%20-%20Emacs%20Voice%20Control%20With%20Local%20AI%20on%20a%202018%20Laptop%20-%20Sacha%20Chua's%20Setup.vtt">Download the VTT</a>
</p>

</div>

<p>
He hasn't released a clip of this one yet, but this exchange was one of my favourite parts of the main conversation:
</p>

<blockquote>
<p>
[Linkarzu]: I don't know if like that's a particularity of Neovim users, we're all toxic and we just hate each other.
</p>

<p>
[Sacha]: Yeah, maybe you should just come over to the Emacs community. We have hot chocolate.
</p>

<p>
[Linkarzu]: If you go to the Emacs subreddit,
it's like rainbows and butterflies.
There's not a lot of hate.
I always wonder why does that happen?
It's not moderators, you know,
it's just people.
Is it just because it's old people?
Like the professor,
he doesn't want to fight any more.
He's old enough.
He retired already.
He's just ready to pass to the next life.
So what, what?
</p>

<p>
[Sacha]: Hypothesis number two,
maybe your editor makes you grumpy.
</p>
</blockquote>

<p>
That's at about 01:30:06 in this unlisted embed of the full conversation:
</p>

<p>
</p><div class="yt-video"><iframe width="456" height="315" title="YouTube video player" src="https://www.youtube-nocookie.com/embed/BlgtfOsXgCs?enablejsapi=1&start=5406" frameborder="0" allowfullscreen=""></iframe><a href="https://www.youtube.com/watch?v=BlgtfOsXgCs&t=5406">Watch on YouTube</a></div>
<p></p>
<div><a href="https://sachachua.com/blog/2026/10/clip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F10%2Fclip-emacs-voice-control-describing-my-setup-during-linkarzu-s-livestream%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Dictating blog posts into Emacs</title>
		<link>https://sachachua.com/blog/2026/10/dictating-blog-posts-into-emacs/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 19:13:18 GMT</pubDate>
    <category>emacs</category>
<category>speech-recognition</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2026/10/dictating-blog-posts-into-emacs/</guid>
		<description><![CDATA[<p>
<a href="https://mtwb.blog/posts/2026/blogging/a-different-type-of-blog-post/">Matt is experimenting with dictating his blog posts.</a> He says:
</p>

<blockquote>
<p>
I write really well. I have very good grammatical skills, for the most part, when I’m typing. But when I speak, I don’t speak like that. My grammar is not very good. I say words out of order a lot. I have filler words. And I say things that don’t make any sense.
</p>
</blockquote>

<p>
This is totally me too!
It's one of the reasons why I want to get used to dictation, because it'll give me a lot more practice in saying things, which can only help when I'm making videos or talking to people.
</p>

<blockquote>
<p>
While I’m talking, the text that’s outputted has no punctuation or paragraphs or anything like that.
</p>
</blockquote>

<p>
Oh yeah, no, that's mostly a matter of picking the right speech recognition engine.
For example, I'm using a local instance of <a href="https://sachachua.com/blog/2026/09/experimenting-with-orukeet-for-speech-recognition/">Orukeet</a>, and it seems to do a reasonable job of dividing my thoughts into properly-capitalized and punctuated sentences in both English and French.
In fact, I have some code in Emacs that wraps it to one sentence per line (as part of <a href="https://sachachua.com/dotemacs#multimedia-whisper">sacha-whisper-insert</a> which uses <a href="https://github.com/sachac/whisper.el/">sachac/whisper.el</a>).
This allows me to use Org shortcuts to move lines around in order to reorganize them into something that makes more sense.
</p>

<p>
Some people use large language models to clean up raw transcripts and remove false starts or filler words.
I don't quite trust large language models to leave my wording alone.
I have some code that uses <code>replace-regexp-in-string</code> to take out some of my filler words and fix commonly misrecognized words that I've saved with <code>substitute.el</code>.
</p>

<p>
I'm not sure if I was ever any good at multitasking, but nowadays at 43 years old, I find that talking and typing at the same time has gotten a little bit harder for me.
I type much more slowly if I want to narrate things at the same time,
like when I'm recording a video or live streaming.
When I'm just thinking on my own, I type quickly enough that my computer sometimes jumbles the keystrokes up because it doesn't handle keyboard interrupts well when it's busy.
It's not just my fingers; it sometimes happens even when I was trying to use wdotool to simulate typing.
Speech recognition is surprisingly reliable, even though I have to correct it from time to time.
The broad strokes are there, and I save the recordings so that I can fix misrecognized words.
</p>

<p>
I also love the way that speech recognition opens up so many more possibilities
for commands I would never be able to memorize keyboard shortcuts for. "Remind me today to&hellip;" is now one of my favourite ways to capture an Org Mode task.
</p>

<p>
Besides, I think it's a good idea to plan ahead just in case I can't type very well in the future. All sorts of things can happen: accidents, disease, old age.
I wrote about that a bit more in <a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/">Emacs Carnival Oct 2026: Exploring keys and voice input in Emacs</a>.
</p>

<p>
All that to say: exciting times ahead.
I think it would be pretty amazing if this sort of thing allows more people to write and share.
Looking forward to reading (and possibly listening to) what people have been learning about!
</p>
<div><a href="https://sachachua.com/blog/2026/10/dictating-blog-posts-into-emacs/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F10%2Fdictating-blog-posts-into-emacs%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Des commandes vocales pour ajouter des tâches sur Emacs</title>
		<link>https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Wed, 07 Oct 2026 19:52:45 GMT</pubDate>
    <category>emacs</category>
<category>org</category>
<category>speech</category>
<category>speech-recognition</category>
<category>french</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/</guid>
		<description><![CDATA[<p>
</p><div class="yt-video"><iframe width="456" height="315" title="YouTube video player" src="https://www.youtube-nocookie.com/embed/rGAXW9S4AiE?enablejsapi=1" frameborder="0" allowfullscreen=""></iframe><a href="https://youtu.be/rGAXW9S4AiE">Watch on YouTube</a></div>
<p></p>

<p>
Liens :
</p>

<ul class="org-ul">
<li><a href="https://sachachua.com/blog/2026/09/experimenting-with-orukeet-for-speech-recognition/">Orukeet</a> pour la reconnaissance vocale parce qu'il est assez rapide et assez précis.</li>
<li><a href="https://github.com/sachac/whisper.el/">sachac/whisper.el</a></li>
<li><a href="https://codeberg.org/sachac/speech-input">speech-input</a></li>
</ul>

<p>
</p><div class="full-transcript"><p></p><div class="transcript-heading"><span class="audio-time" data-start="0.000000">0:00</span> <strong>Introduction</strong></div><p></p><span class="audio-time caption" data-speaker="" data-start="0.000000" data-stop="3.047000">Je veux vous présenter ma petite expérience</span> <span class="audio-time caption" data-speaker="" data-start="3.047000" data-stop="4.879000">de la reconnaissance vocale</span> <span class="audio-time caption" data-speaker="" data-start="4.879000" data-stop="6.778000">pour dicter dans Emacs</span> <span class="audio-time caption" data-speaker="" data-start="6.778000" data-stop="8.477000">et pour les contrôler.</span> <span class="audio-time caption" data-speaker="" data-start="8.477000" data-stop="10.444000">J'utilise le modèle Orukeet</span> <span class="audio-time caption" data-speaker="" data-start="10.444000" data-stop="12.876000">pour la reconnaissance vocale parce qu'il est</span> <span class="audio-time caption" data-speaker="" data-start="12.876000" data-stop="15.415000">assez rapide et assez précis.</span> <span class="audio-time caption" data-speaker="" data-start="15.415000" data-stop="18.335000">Je l'intègre dans Emacs en utilisant</span> <span class="audio-time caption" data-speaker="" data-start="18.335000" data-stop="22.034000">mes bibliothèques sachac/whisper.el et speech-input.</span> <span class="audio-time caption" data-speaker="" data-start="22.034000" data-stop="24.427000">J'ai récemment créé des commandes vocales</span> <span class="audio-time caption" data-speaker="" data-start="24.427000" data-stop="28.273000">pour ajouter des éléments à ma liste de tâches.</span> <span class="audio-time caption" data-speaker="" data-start="28.273000" data-stop="31.326000">C'est étonnement utile parce que ça réduit</span> <span class="audio-time caption" data-speaker="" data-start="31.326000" data-stop="34.325000">la charge cognitive liée à la mémorisation</span> <span class="audio-time caption" data-speaker="" data-start="34.325000" data-stop="35.951000">des raccourcis clavier.</span> <span class="audio-time caption" data-speaker="" data-start="35.951000" data-stop="37.551000">Je vais vous montrer cela.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="37.000000">0:37</span> <strong>Ajouter des tâches
</strong></div><p></p><a href="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-00-58-833.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-00-58-833.jpg" alt="image from video " data-time=""></a><span class="audio-time caption" data-speaker="" data-start="37.551000" data-stop="40.124000">Je presse la touche zéro du pavé numérique</span> <span class="audio-time caption" data-speaker="" data-start="40.124000" data-stop="42.390000">avant et après avoir dit</span> <span class="audio-time caption" data-speaker="" data-start="42.390000" data-stop="46.422000">« Rappelle-moi demain de préparer</span> <span class="audio-time caption" data-speaker="" data-start="46.422000" data-stop="48.668000">une présentation pour Emacs Paris</span> <span class="audio-time caption" data-speaker="" data-start="48.668000" data-stop="58.108000">sur les commandes vocales. »</span> <span class="audio-time caption" data-speaker="" data-start="58.108000" data-stop="63.228000">La fonction crée une tâche dans ma boîte de réception.</span> <span class="audio-time caption" data-speaker="" data-start="63.228000" data-stop="66.428000">La tâche est planifiée pour demain.</span> <span class="audio-time caption" data-speaker="" data-start="66.428000" data-stop="67.621000">Il y a un lien</span> <span class="audio-time caption" data-speaker="" data-start="67.621000" data-stop="69.900000">vers l'enregistrement</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="69.000000">1:09</span> <strong>D'autres dates
</strong></div><p></p><a href="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-02-26-667.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-02-26-667.jpg" alt="image from video " data-time=""></a><span class="audio-time caption" data-speaker="" data-start="69.900000" data-stop="73.494000">« Rappelez-moi demain de préparer une présentation</span> <span class="audio-time caption" data-speaker="" data-start="73.494000" data-stop="81.680000">pour Emacs Paris sur les commandes vocales »,</span> <span class="audio-time caption" data-speaker="" data-start="81.680000" data-stop="88.940000">la capture d'écran et</span> <span class="audio-time caption" data-speaker="" data-start="88.940000" data-stop="90.240000">le contexte de tâche.</span> <span class="audio-time caption" data-speaker="" data-start="90.240000" data-stop="93.294000">Ça m'aident si j'ai été mal comprise</span> <span class="audio-time caption" data-speaker="" data-start="93.294000" data-stop="96.239000">ou si j'ai besoin de contexte.</span> <span class="audio-time caption" data-speaker="" data-start="96.239000" data-stop="97.327000">De la même manière,</span> <span class="audio-time caption" data-speaker="" data-start="97.327000" data-stop="100.526000">je peux dire « Rappelle-moi la semaine prochaine</span> <span class="audio-time caption" data-speaker="" data-start="100.526000" data-stop="110.237000">de faire la paperasse pour l'école. »</span> <span class="audio-time caption" data-speaker="" data-start="110.237000" data-stop="112.444000">« Rappelle-moi aujourd'hui de créer</span> <span class="audio-time caption" data-speaker="" data-start="112.444000" data-stop="119.376000">une vidéo sur mes commandes vocales. »</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="119.000000">1:59</span> <strong>Basculer à une tâche
</strong></div><p></p><a href="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-02-45-867.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-02-45-867.jpg" alt="image from video " data-time=""></a><span class="audio-time caption" data-speaker="" data-start="119.376000" data-stop="122.296000">Je peux aussi créer une tâche et déclencher</span> <span class="audio-time caption" data-speaker="" data-start="122.296000" data-stop="125.455000">le suivi du temps (ou clock in).</span> <span class="audio-time caption" data-speaker="" data-start="125.455000" data-stop="127.095000">Par exemple, je peux dire</span> <span class="audio-time caption" data-speaker="" data-start="127.095000" data-stop="135.474000">« Commence à implémenter la génération des questions. »</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="135.000000">2:15</span> <strong>Retrouver le fil de mes pensées
</strong></div><p></p><a href="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-03-07-000.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-03-07-000.jpg" alt="image from video " data-time=""></a><span class="audio-time caption" data-speaker="" data-start="135.474000" data-stop="138.107000">Si je perds le fil de mes pensées :</span> <span class="audio-time caption" data-speaker="" data-start="138.107000" data-stop="142.307000">« j'en étais où ? »</span> <span class="audio-time caption" data-speaker="" data-start="142.307000" data-stop="147.227000">Ça m'affiche les tâches récentes.</span> <p></p><div class="transcript-heading"><span class="audio-time" data-start="147.000000">2:27</span> <strong>Je faisais quoi ?
</strong></div><p></p><a href="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-03-27-333.jpg"><img loading="lazy" target="_blank" src="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/2026-10-07_14-11-23-00-03-27-333.jpg" alt="image from video " data-time=""></a><span class="audio-time caption" data-speaker="" data-start="147.227000" data-stop="150.747000">« Je faisais quoi ? »</span> <span class="audio-time caption" data-speaker="" data-start="150.747000" data-stop="158.846000">Ça bascule sur la tâche actuelle.</span> <span class="audio-time caption" data-speaker="" data-start="158.846000" data-stop="163.226000">Voilà des commandes vocales pour ajouter des tâches.</span></div>
<p></p>
<div><a href="https://sachachua.com/blog/2026/10/des-commandes-vocales-sur-emacs-pour-ajouter-des-taches/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F10%2Fdes-commandes-vocales-sur-emacs-pour-ajouter-des-taches%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Emacs Carnival Oct 2026: Exploring keys and voice input in Emacs</title>
		<link>https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Wed, 07 Oct 2026 12:18:31 GMT</pubDate>
    <category>emacs</category>
<category>speech-recognition</category>
<category>speech</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/</guid>
		<description><![CDATA[<div class="sticky-toc" id="org324d2fb">
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-making-more-use-of-the-keyboard">Making more use of the keyboard</a></li>
<li><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-some-reasons-why-i-m-curious-about-speech-recognition">Some reasons why I'm curious about speech recognition</a></li>
<li><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-quick-benefits-from-voice-commands">Quick benefits from voice commands</a></li>
<li><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-prior-work-on-voice-control-of-emacs-voicemacs-emacslisten">Prior work on voice control of Emacs: Voicemacs, Emacslisten</a></li>
<li><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-imagining-a-hybrid-approach-keyboard-voice-even-mouse-stylus">Imagining a hybrid approach: keyboard, voice, even mouse/stylus?</a></li>
</ul>
</div>

</div>

<div class="update" id="org73d9815">
<p>
<span class="timestamp-wrapper"><time class="timestamp" datetime="2026-10-29">[2026-10-29 Thu]</time></span>: Fixed my orderless matching, so Talon command completions might work better now.
</p>

</div>
<div id="outline-container-emacs-carnival-exploring-keys-and-voice-input-in-emacs-making-more-use-of-the-keyboard" class="outline-3">
<h3 id="emacs-carnival-exploring-keys-and-voice-input-in-emacs-making-more-use-of-the-keyboard"><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-making-more-use-of-the-keyboard">Making more use of the keyboard</a></h3>
<div class="outline-text-3" id="text-emacs-carnival-exploring-keys-and-voice-input-in-emacs-making-more-use-of-the-keyboard">
<p>
October's Emacs Carnival theme "<a href="https://www.emacswiki.org/emacs/CarnivalOctober2026">Are keyboards and M-x here to stay?</a>" is a good nudge to think about the voice interface I've been experimenting with and where I want to take it.
</p>

<p>
<a href="https://chrismaiorana.com/m-x-magic/">Like Chris Maiorana</a>, I love the way Emacs's <code>M-x</code> (<code>execute-extended-command</code>) allows me to call any command by name.
It means that I can call commands or come up with my own commands without worrying about assigning them to keyboard shortcuts or making sense of where to place them in a hierarchical menu.
With flexible completion via the <a href="https://github.com/minad/vertico">vertico</a> and <a href="https://github.com/oantolin/orderless">orderless</a> packages, I don’t even need to type in the full name of the command or remember the words in the right order.
If I still can’t remember the command, it’s easy enough to use defalias to make my own nickname for it.
</p>


<figure id="org97e5320">
<a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-06_11-04-07.png"><img src="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-06_11-04-07.png" alt="2026-10-06_11-04-07.png"></a>

<figcaption><span class="figure-number">Figure 1: </span>I can filter M-x to show just my custom commands</figcaption>
</figure>

<p>
I tend to limit keyboard shortcuts to only my most common commands.
Everything else is hard to practise enough so that it sticks in my muscle memory.
Even with <a href="https://github.com/minad/marginalia">marginalia</a> showing me keybindings when I use <code>M-x</code> and <a href="https://emacsredux.com/blog/2026/07/18/which-key-is-now-built-in/">which-key</a> showing me hints when I pause, it doesn’t sink in.
</p>


<figure id="org660a018">
<a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-06_11-05-00.png"><img src="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-06_11-05-00.png" alt="2026-10-06_11-05-00.png"></a>

<figcaption><span class="figure-number">Figure 2: </span>Marginalia shows keybindings</figcaption>
</figure>

<p>
Maybe I could use <a href="https://github.com/federicotdn/key-quiz">key-quiz</a> to drill a small number of keyboard shortcuts I’m actively working on learning, or modify <code>help-quick-sections</code> to show me reminders when I use <code>C-h C-q</code> (<code>help-quick-toggle</code>).
</p>

<p>
Getting one keyboard shortcut to do the right thing in different
contexts is great because it means fewer shortcuts to memorize. Sometimes I can do that by binding the right functions to different mode maps with <code>bind-key</code>, like the way my <a href="https://sachachua.com/blog/2025/09/adding-org-mode-link-awesomeness-elsewhere-my-org-insert-link-dwim/">sacha-org-insert-link-dwim</a> uses the right link syntax in Markdown or OddMuse too.
I also like the context-sensitive keyboard shortcuts I get from <a href="https://github.com/oantolin/embark">Embark</a>.
I find myself using a handful of those regularly, and <code>C-.</code>
(<code>embark-act</code>) starts a bunch of shortcuts across different types of
things: <code>i</code> to insert, <code>w</code> to copy, and so on.
For example, I know <code>C-x C-f</code> (<code>find-file</code>) will let me select a file to open.
Instead of opening it, I can use <code>C-. i</code> to insert the filename into the buffer.
</p>

<p>
Simpler keyboard shortcuts help. I rarely use the numpad on my laptop keyboard, so those keys can be mapped to functions that don't need to be followed by lots of typing. For example, 0 is a large button. I use it to start and stop speech recognition. Actually, I bind f9 in Emacs to <code>sacha-whisper-run-at-point</code>, and I bind numpad-0 as a keyboard shortcut in KDE (my window manager) instead of just in Emacs so that it's available globally. I'm still on X11 instead of Wayland. The shell script <a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/xdotool-emacs">xdotool-emacs</a> focuses Emacs briefly and sends the keys, and the shortcut is set to <code>/home/sacha/bin/xdotool-emacs key &#45;&#45;clearmodifiers F9</code>. I do that instead of using emacsclient because emacsclient starts a different buffer, and I might want to keep the context. My <a href="https://sachachua.com/dotemacs#multimedia-whisper">sacha-whisper-maybe-type</a> function detects if Emacs is not the focused application and types the text if it isn't, which means I can use it as keyboard input for oher applications while still taking advantage of the corrections and commands I've defined in Emacs. An easy-to-press global shortcut means I can use it from anywhere, even when my foreground app is the browser or another application.
</p>

<p>
An additional modifier opens up more possibilities for keyboard shortcuts near the regular typing position. My Windows key acts like a Super modifier. I've set it up as a one-shot modifier with Kanata so that I can either hold it down to access more shortcuts or tap it to treat it like a leader key.
</p>

<p>
There are quite a few commands for which neither M-x nor keyboard shortcuts feel like the right fit: things I want to do faster or more fluidly than M-x, but which I don't do often enough to memorize a good shortcut for. I've been experimenting a bit with voice input, and I really like it.
</p>
</div>
</div>
<div id="outline-container-emacs-carnival-exploring-keys-and-voice-input-in-emacs-some-reasons-why-i-m-curious-about-speech-recognition" class="outline-3">
<h3 id="emacs-carnival-exploring-keys-and-voice-input-in-emacs-some-reasons-why-i-m-curious-about-speech-recognition"><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-some-reasons-why-i-m-curious-about-speech-recognition">Some reasons why I'm curious about speech recognition</a></h3>
<div class="outline-text-3" id="text-emacs-carnival-exploring-keys-and-voice-input-in-emacs-some-reasons-why-i-m-curious-about-speech-recognition">
<ul class="org-ul">
<li>I could use more practice speaking, since I don't talk to a lot of people in real life.
<ul class="org-ul">
<li><b>English:</b> Videos and livestreams can be a way to share interesting things and start conversations. I want to organize my thoughts, improve my breathing, and reduce my stutters and false starts. That will make the audio more pleasant to listen to. Dictating to my computer gives me more practice. It also makes things easier to livestream if I can reuse my speech as input.</li>
<li><p>
<b>French:</b> I like challenging my brain and I want to help the kiddo learn it too. Improving my speaking will also make it easier for me to listen to myself and close the feedback loop. Maybe I can even use speech recognition to give me feedback and increase motivation while speaking, like <a href="https://sachachua.com/dotemacs#writing-and-editing-speech-recognition-reporting-speaking-stats-balance">keeping track of words or phrases I want to use</a>. When I use them in conversation, the buffer can cross them off the list, which lets me focus on the words remaining.
</p>

<figure id="orgd283055">
<img src="https://sachachua.com/blog/2026/10/using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time/2026-10-07_07-33-03.png" alt="2026-10-07_07-33-03.png">

<figcaption><span class="figure-number">Figure 3: </span>Screenshot showing balance and target words</figcaption>
</figure></li>
</ul></li>
<li>Dictation can capture ideas quickly, with more natural wording, especially since typing sometimes gets messed up on my computer.</li>
<li>Voice interfaces might let me reduce some kinds of task friction or work hands-free. For example, I like setting timers by voice on my phone while cooking. It might be interesting to be able to dictate a draft while knitting or hand-sewing.</li>
<li>Brain fog is something I will likely need to deal with. I got a preview of it thanks to the sleep deprivation of early parenting. I know that brain fog is probably going to happen around menopause, it will probably also be part of aging, and it's a common symptom of long COVID which I might still end up getting despite my precautions. I could get into an accident and hurt my hands. I like Emacs and I want to be able to keep using it even if cognitive or physical changes make it harder to remember or use lots of keyboard shortcuts. If I start playing around with this now, the capabilities and the habits will be there when I need it.</li>
<li>Other people are already dealing with those kinds of physical or cognitive constraints, so things I figure out might be useful for other people.</li>
<li>A global shortcut for voice notes and commands let me quickly dash off a reminder before I take off. I don't have to find Emacs and go through my Org capture templates.</li>
<li>It's fun. I like saying "Prepare for take-off" and having Emacs get ready for a spontaneous stream by updating YouTube using my current Org Mode subtree title and description. I like seeing if my dictionary of substitutions can fix the commonly misrecognized words. Saying "I command you to&hellip;" to get Emacs to do a M-x command is totally inefficient but also playful, and silly things stick in my brain better thanks to the mnemonic advantage of humor. My brain likes that kind of novelty.</li>
</ul>

<p>
For context, I use Linux on a Lenovo P52 laptop (manufactured 2018, 64 GB RAM but hardly any GPU), and I have an Android phone. I like writing regular text, Emacs Lisp, and some Python and JavaScript. I'm slowly starting to have more focused time now that the kiddo is in virtual school, although it's still broken up by recess and lunch. I will probably have an increasing amount of focused time over the next few years.
</p>
</div>
</div>
<div id="outline-container-emacs-carnival-exploring-keys-and-voice-input-in-emacs-quick-benefits-from-voice-commands" class="outline-3">
<h3 id="emacs-carnival-exploring-keys-and-voice-input-in-emacs-quick-benefits-from-voice-commands"><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-quick-benefits-from-voice-commands">Quick benefits from voice commands</a></h3>
<div class="outline-text-3" id="text-emacs-carnival-exploring-keys-and-voice-input-in-emacs-quick-benefits-from-voice-commands">
<p>
I love the way voice reduces context shifts. For example, some of my most-used voice commands are:
</p>

<ul class="org-ul">
<li><p>
remind me [today/tomorrow/next week/next month/someday] to&hellip;: capture and schedule the task; include an Org link and screenshot for context, and link to the audio recording in case the text was misrecognized
</p>


<figure id="org30c8d24">
<a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-07_10-00-55.png"><img src="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-07_10-00-55.png" alt="2026-10-07_10-00-55.png"></a>

<figcaption><span class="figure-number">Figure 4: </span>Captured tasks have an audio recording, a screenshot, and a link</figcaption>
</figure></li>

<li>switch to&hellip;: capture the task and immediately clock into it</li>
<li>what was I doing?: jump to the current task</li>
<li><p>
where was I?: show recently-clocked tasks
</p>

<figure id="orga04e9e3">
<a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-07_10-02-00.png"><img src="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/2026-10-07_10-02-00.png" alt="2026-10-07_10-02-00.png"></a>

<figcaption><span class="figure-number">Figure 5: </span>This is the same as C-u M-x org-clock-goto</figcaption>
</figure></li>
</ul>

<p>
I don't have to think about which keys I've configured in <code>org-capture-templates</code>. I just use a natural phrase.
</p>

<p>
I've defined "open &hellip;" to open things from my <a href="https://sachachua.com/dotemacs/index.html#org-bookmarks">bookmarked links stored in an Org file</a>. Because my numpad-0 keybinding works from anywhere, I can even use it to open sites or capture notes while looking at my browser.
</p>

<p>
Because Emacs can interact with lots of other applications, I can even experiment with controlling those applications by voice. For example, I use obs-websocket.el to control OBS for streaming or recording a video. Using "Let's go live" and "Over and out" to start or stop streaming or "Start recording" and "Stop recording" to manage the recording means I can do that without needing to find the right window. I stream with a 10-second delay so that there's a little bit of time to catch any private information that's been accidentally shared on the screen. My new "panic" command lets me discard that delay and interrupt the stream by using an OBS keyboard shortcut to kill the stream immediately, even if I'm probably not going to remember that exact keyboard shortcut when I'm stressed. It should be a little easier to remember that than a keyboard shortcut I hardly ever use or a menu item that is a really small target (and my Emacs might not even be my foreground app).
</p>

<p>
For regular dictation, I'm experimenting with having it automatically place each sentence on a new line or list item. This makes it easy to reorganize in Org. I can refill it into a paragraph when I'm done.
</p>

<p>
I still have to deal with the limits of my memory, of course.
I have a "what can I say" command that opens up an Org file with my notes. I keep this as a manual note instead of just dumping the contents of my <code>sacha-whisper-commands</code> variable so that I can prioritize the things I'm trying to build as habits.
</p>

<p>
<a href="https://sachachua.com/dotemacs#writing-and-editing-speech-recognition-slowly-building-speech-based-commands-for-emacs">Here's the part of my config that sets up some speech commands</a>.
</p>

<p>
I might want more complex grammar someday for things that regular expressions can't easily handle.
That's something other projects have done a good job of figuring out.
</p>
</div>
</div>
<div id="outline-container-emacs-carnival-exploring-keys-and-voice-input-in-emacs-prior-work-on-voice-control-of-emacs-voicemacs-emacslisten" class="outline-3">
<h3 id="emacs-carnival-exploring-keys-and-voice-input-in-emacs-prior-work-on-voice-control-of-emacs-voicemacs-emacslisten"><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-prior-work-on-voice-control-of-emacs-voicemacs-emacslisten">Prior work on voice control of Emacs: Voicemacs, Emacslisten</a></h3>
<div class="outline-text-3" id="text-emacs-carnival-exploring-keys-and-voice-input-in-emacs-prior-work-on-voice-control-of-emacs-voicemacs-emacslisten">
<p>
There's been a lot of good work on voice input over the years, especially since a number of Emacs users have a hard time with or would like to avoid repetitive stress injury. In addition to the innumerable projects that let you use Whisper or similar speech recognition engines to dictate prose, there are also a few projects that use a constrained vocabulary to more reliably control Emacs. Two projects in this area are based on the <a href="https://talonvoice.com/">Talon</a> voice control system: <a href="https://github.com/jcaw/voicemacs">Voicemacs</a> and <a href="https://emacslisten.org/">Emacslisten</a>.
</p>

<ul class="org-ul">
<li>The <a href="https://github.com/talonhub/community">Talon community hub</a> defines some Emacs commands. I think it simulates keyboard input and assumes that you have <a href="https://github.com/lewang/flx">flx</a> or some other fuzzy completion matching so that input like <code>M-x t-d-on-e</code> runs <code>toggle-debug-on-error</code>, as defined in <a href="https://github.com/talonhub/community/blob/main/apps/emacs/emacs_commands.csv">community/apps/emacs/emacs_commands.csv</a>. Update <span class="timestamp-wrapper"><time class="timestamp" datetime="2026-10-29">[2026-10-29 Thu]</time></span>: I removed that column of the CSV from my <a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/emacs_commands.csv">emacs_commands.csv</a> because I was having problems with completion, which turned out to be a bug in my <a href="https://sachachua.com/dotemacs#completion-emacs-completion-and-handling-accented-characters-with-orderless">orderless code for accents</a>, so I think this works regularly now.</li>
<li><a href="https://emacslisten.org/">Emacslisten</a> uses <a href="https://github.com/ErikPrantare/phony.el">Emacs Lisp macros</a> to define the command vocabulary. <a href="https://github.com/ErikPrantare/cursorfree.el">Cursorfree</a> defines a whole bunch of them, using <a href="https://github.com/ErikPrantare/hatty.el">hatty</a> for voice navigation within a buffer.</li>
<li><a href="https://github.com/jcaw/voicemacs">Voicemacs</a> defines its commands in Python in the user's Talon config (<a href="https://github.com/jcaw/talon_config/tree/master/emacs">see example</a>). It feels fairly complex, like it assumes it's being cloned as your <code>~/.talon/user</code> directory. It lets Emacs and Talon communicate via RPC to send additional context. It also extends some things like dired and company to make them more voice-friendly.</li>
</ul>

<p>
I'm new to both projects, so I'm not sure if I'm reading this right, but I think I might be able to use Emacslisten to define voice commands with Emacs Lisp, and then maybe someday use Voicemacs to provide richer context for some of those commands. For example, Voicemacs can list snippets, but I'm not quite sure how to use that yet.
</p>

<p>
It looks like I can use Talon for commands and my whisper.el setup for regular dictation at the same time. The Pipewire virtual sinks help with that, I think, since the programs don't have to have an exclusive lock on the audio device. This means that I can explore the kinds of vocabularies that people have set up while also taking advantage of greater accuracy and ease of correction in my whisper dictation workflow. I think I can use the Talon process socket to turn off recognition when I want to dictate using my whisper.el setup, and then I can turn it back on afterwards.
</p>

<p>
A note about the underlying speech recognition: <a href="https://talonvoice.com/">Talon</a> is cross-platform and free, but not open source. Subscribing to Talon (CAD 36/month) lets you use a Conformer + Whisper hybrid engine that uses whisper-large for more accuracy, but that's a bit on the expensive side for me. <a href="https://github.com/dictation-toolbox/dragonfly">Dragonfly</a> is a free open source engine that can use Kaldi or CMU Pocket Sphinx for speech recognition, but I'm not sure what the performance is like.
</p>

<p>
Interestingly, because Talon simulates keystrokes, using the commands from the community set means keys sometimes arrive out of order on my computer. "split solo" is supposed to run <code>M-x delete-other-windows</code>, but it sometimes ends up sending <code>M-x delete-other-wnidows</code> instead. This is the same sort of annoyance that gets in the way of typing quickly on my computer. Keystrokes get processed out of order when the CPU gets busy, which is part of the reason why I find dictation to be more reliable than typing sometimes. I should fix that someday, but my attempts so far aren't working. (I wonder if it has something to do with the GPU under Linux; maybe I should drop back down to CPU only since it turns out I can't use the GPU for much anyway.) Quick fix, let's try adding a brief delay in my <a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/settings.talon">settings.talon</a>:
</p>

<pre class="example" id="org48cf84a">
settings():
    # Adds a small pause (in ms) between each character typed
    insert_wait = 5

    # Ensures modifiers (Shift/Ctrl) don't un-latch too fast
    key_wait = 1.0
</pre>

<p>
I think Voicemacs uses a sevver and and Emacslisten uses emacsclient to communicate with Emacs directly, so they probably won't run into this keystroke problem.
</p>

<p>
It might be interesting to see if I can port some of my simpler commands over to Emacslisten or straight Talon so that they're always active. Then I can slowly work on getting used to the other commands people find useful, or incorporating them into my setup.
</p>
</div>
</div>
<div id="outline-container-emacs-carnival-exploring-keys-and-voice-input-in-emacs-imagining-a-hybrid-approach-keyboard-voice-even-mouse-stylus" class="outline-3">
<h3 id="emacs-carnival-exploring-keys-and-voice-input-in-emacs-imagining-a-hybrid-approach-keyboard-voice-even-mouse-stylus"><a href="https://sachachua.com/blog/feed/index.xml#emacs-carnival-exploring-keys-and-voice-input-in-emacs-imagining-a-hybrid-approach-keyboard-voice-even-mouse-stylus">Imagining a hybrid approach: keyboard, voice, even mouse/stylus?</a></h3>
<div class="outline-text-3" id="text-emacs-carnival-exploring-keys-and-voice-input-in-emacs-imagining-a-hybrid-approach-keyboard-voice-even-mouse-stylus">
<p>
I love shifting fluidly from one form of input to another, depending on what makes sense at that moment.
</p>

<p>
For example, <a href="https://sachachua.com/blog/2025/07/finding-the-shape-of-my-thoughts/">I often start my thoughts</a> by drawing on a light dotted grid using Noteful on my iPad. That lets me quickly cluster ideas spatially, drawing connections and highlighting words as I figure out what I want to say.
</p>

<p>
</p><div class="sketch-full"><a class="photoswipe" href="https://sketches.sachachua.com/filename/2026-10-07-01%20Experimenting%20with%20voice%20input%20in%20Emacs%20-%20rough%20braindump.jpeg" data-src="https://sketches.sachachua.com/static/2026-10-07-01%20Experimenting%20with%20voice%20input%20in%20Emacs%20-%20rough%20braindump.jpeg" data-title="2026-10-07-01 Experimenting with voice input in Emacs - rough braindump.jpeg" data-w="2900" data-h="2050"><picture>
      <img src="https://sketches.sachachua.com/static/2026-10-07-01%20Experimenting%20with%20voice%20input%20in%20Emacs%20-%20rough%20braindump.jpeg" width="2900" height="2050" alt="2026-10-07-01 Experimenting with voice input in Emacs - rough braindump.jpeg" loading="lazy" style="max-height: 90vw; height: auto; width: auto" decoding="async">
      <figcaption>2026-10-07-01 Experimenting with voice input in Emacs - rough braindump.jpeg</figcaption>
    </picture></a></div>
<p></p>

<p>
Then I use dictation and typing to sketch the outline, jumping around as thoughts occur to me. If I'm livestreaming at the same time, that helps me demonstrate things along the way and lets me benefit from people's questions. Dictation lets me explain things to people and capture the text at the same time.
</p>

<p>
I can move around with the keyboard, but it would be really cool to use voice for quick navigation between sections of my outline using either line numbers or words. I've implemented a "line" command that lets me jump to a line using the last two digits of its line number, since I probably won't have more than 100 lines visible on the screen at a time. For example, this line is currently 119, so referring to it as "nineteen" would work. I'd like to add more operations for editing the text. I think it would be great to be able to say "kill line 33" to delete that line or "move lines 16 to 20 after 27" to have it move those lines there.
</p>

<p>
I now have a <code>sacha-whisper-one-sentence-per-line</code> variable that splits up dictation if non-nil, so we'll see where those take me. Orukeet does a reasonable job of punctuating my sentences if I don't pause too much between phrases. <a href="https://sachachua.com/blog/2025/09/emacs-cycle-through-different-paragraph-formats-all-on-one-line-wrapped-max-one-sentence-per-line-one-sentence-per-line/">My M-q re-wraps paragraphs</a> between one sentence per line, one paragraph per line, and wrapping to fill-column, and it's probably the sort of thing that more voice commands would make even more convenient.
</p>

<p>
Maybe I can even use that spatial sense of a layout by touching buttons in an Emacs-served webpage on my tablet to direct my dictation to different places. <a href="https://github.com/larrasket/org-draw">Org-draw</a> uses a similar Emacs web server approach to let you draw on another device and embed the results into an Org file, and there are also simpler HTTP servers (<a target="_blank" href="https://elpa.gnu.org/packages/web-server.html">web-server</a>, <a target="_blank" href="https://elpa.nongnu.org/nongnu/simple-httpd.html">simple-httpd</a>), so it's probably quite doable. I'm imagining something that takes the top-level items from my current nested list and makes them nice big buttons, so I could have something like "Introduction", "Keyboard", "Reasons", "Experiment", "Hybrid", and "Conclusion", and clicking on those could trigger a POST request to the server inside Emacs which moves my point accordingly. I might even have buttons for various commands. It sounds pretty straightforward to implement.
</p>

<p>
When my brain comes up with lots of interesting ideas, my org-capture voice commands let me choose which rabbit holes to postpone and which ones I want to dive into.
</p>

<p>
If I get interrupted by life, I can write on my phone while waiting for the kiddo. <a href="https://www.orgzlyrevived.com/">Orgzly Revived</a> and <a href="https://syncthing.net/">Syncthing</a> let me pick up where I left off.
</p>

<p>
As I get thoughts down, I can use keyboard shortcuts to rearrange things and then convert the list of sentences into paragraphs. <a href="https://sachachua.com/blog/2025/07/finding-unlinked-text/">I can easily turn text into links</a> using my favourites.
</p>

<p>
I can imagine combining the audio clips from the text properties with the videos and screenshots to create a quick narrated clip that I can edit (using Emacs, of course), or publishing the narration so that people can listen to it if they like.
</p>

<p>
This combination of drawing + audio + text works better for me than any of those components alone. Visuals give me anchors for my thoughts. Audio helps with the braindump. Text permits precise, searchable details we can copy. Like <a href="https://curious.port111.com/2026/08/22/will-voice-kill-the-keyboard.html">George Jones</a>, I find they all have their uses and their strengths.
</p>

<p>
When I try to dictate longer sections without that kind of visual support, I often lose my train of thought. I want to see where I've been and where I want to go. I don't trust LLMs to clean up that kind of rough transcript; the output doesn't quite resonate with me. I like keeping speech recognition constrained to sentences I can immediately check or reorganize, and I have the recordings as text properties so I can go back to that segment if I need to (at least during the current editing section). That way, I'm never left scratching my head and wondering what I meant.
</p>

<p>
I love that local speech recognition is getting better and better. I'm curious about whether small language models can do better turn detection to help compensate for my intra-phrase delays when I'm thinking of a word, and whether decision models can automatically classify a sentence into its place in my outline. I think it would be cool if embeddings or other language models could suggest related words or resources as I go along. Lots of interesting possibilities even with the technology that's already available.
</p>

<p>
Thanks to George Jones for hosting <a href="https://www.emacswiki.org/emacs/CarnivalOctober2026">this month's Emacs Carnival on input alternatives</a>. Check out the page for more posts!
</p>
</div>
</div>
<div><a href="https://sachachua.com/blog/2026/10/emacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F10%2Femacs-carnival-oct-2026-exploring-keys-and-voice-input-in-emacs%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Using Emacs to visualize speaking and target words/phrases in real-ish time</title>
		<link>https://sachachua.com/blog/2026/10/using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Wed, 07 Oct 2026 11:47:04 GMT</pubDate>
    <category>audio</category>
<category>speech-recognition</category>
<category>emacs</category>
<category>learning</category>
<category>visualization</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2026/10/using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time/</guid>
		<description><![CDATA[<div class="sticky-toc" id="org60bfade">
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="https://sachachua.com/blog/feed/index.xml#writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-voice-activity-detection-and-speech-recognition">1. Voice activity detection and speech recognition</a></li>
<li><a href="https://sachachua.com/blog/feed/index.xml#writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-noticing-bingo-phrases">2. Noticing bingo phrases</a></li>
</ul>
</div>

</div>


<figure id="org4276db7">
<img src="https://sachachua.com/blog/2026/10/using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time/2026-10-07_07-33-03.png" alt="2026-10-07_07-33-03.png">

<figcaption><span class="figure-number">Figure 1: </span>Screenshot showing my list of target phrases and which ones have been recognized</figcaption>
</figure>

<p>
When I'm working with a tutor to improve my French, I sometimes need
to push myself to provide more details when I'm sharing and ask more
follow-up questions in response to what they're saying, instead of
falling back to « D'accord » and « C'est cool. » Visualizing the
timeline in real-time lets me see if the turn-taking looks good. I
might even need to take a more active role in controlling the
conversation if I happen to be meeting a tutor who tends to go on
monologues.
</p>

<p>
When I'm talking out loud on my own for practice, this might also
nudge me to reduce the pauses and enrich my vocabulary by practising
saying things often enough that they become more automatic.
</p>
<div id="outline-container-writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-voice-activity-detection-and-speech-recognition" class="outline-3">
<h3 id="writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-voice-activity-detection-and-speech-recognition"><span class="section-number-3">1.</span> <a href="https://sachachua.com/blog/feed/index.xml#writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-voice-activity-detection-and-speech-recognition">Voice activity detection and speech recognition</a></h3>
<div class="outline-text-3" id="text-writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-voice-activity-detection-and-speech-recognition">
<p>
This code uses this server <a href="https://sachachua.com/blog/2026/10/using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time/asr.py">vad-file.py</a>, which I generated with Google Gemini. It emits JSONL for voice activity detection events (start/stop) and uses Orukeet to transcribe the speech once the voice activity detection notices it's finished. I also use spacy to look up token information because I want to someday colour the nouns by gender, which might allow me to correct myself in the next sentence instead of hesitating in the previous one.
</p>

<details><summary>code for sacha-vad-tutor</summary>
<div class="org-src-container">
<pre class="src src-emacs-lisp"><code><span class="org-comment-delimiter">;; </span><span class="org-comment">Report on the balance between my tutor and me</span>
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-process</span> nil)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-dir</span> <span class="org-string">"~/vendor/orukeet"</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-command</span> <span class="org-highlight-quoted-quote">`</span>(,(expand-file-name <span class="org-string">".venv/bin/python"</span> sacha-vad-tutor-dir)
                                  <span class="org-string">"asr.py"</span>
                                  <span class="org-string">"&#45;&#45;spacy-model"</span>
                                  <span class="org-string">"fr_dep_news_trf"</span>
                                  <span class="org-string">"-s"</span> <span class="org-string">"300"</span> <span class="org-string">"-t"</span>
                                  <span class="org-string">"VirtualMicSink.monitor=sacha"</span> <span class="org-string">"alsa_output.usb-AT_AT2040USB_202011110001-00.analog-stereo.monitor=other"</span>))
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-events</span> nil <span class="org-doc">"List of events as an list of alists (label start stop duration)."</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-labels</span> <span class="org-highlight-quoted-quote">'</span>(<span class="org-string">"sacha"</span> <span class="org-string">"other"</span>))
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-summary</span> <span class="org-highlight-quoted-quote">'</span>((<span class="org-string">"sacha"</span> . 0)
                                  (<span class="org-string">"other"</span> . 0)))
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-session-length</span> (* 60 60) <span class="org-doc">"Number of seconds to use for scale."</span>)

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-update-view</span> ()
  <span class="org-doc">"Visualize the last 3 minutes and the overview of the session.</span>
<span class="org-doc">Also keep track of target phrases."</span>
  (<span class="org-keyword">interactive</span>)
  (<span class="org-keyword">with-current-buffer</span> (get-buffer-create <span class="org-string">"*tutor stats*"</span>)
    (erase-buffer)
    (<span class="org-keyword">let*</span> ((total (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">+</span> (mapcar <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">cdr</span> sacha-vad-tutor-summary)))
           (height 30)
           (width (window-width nil t))
           (max-seconds
            (<span class="org-keyword">if</span> sacha-vad-tutor-events
                (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">max</span> (mapcar (<span class="org-keyword">lambda</span> (o) (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> o))
                                    sacha-vad-tutor-events))
              0))
           (threshold 180)              <span class="org-comment-delimiter">; </span><span class="org-comment">last three minutes</span>
           (threshold-seconds (- max-seconds threshold))
           (recent
            (seq-keep
             (<span class="org-keyword">lambda</span> (o)
               (<span class="org-keyword">when</span> (&gt;= (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">start</span> o)
                         threshold-seconds)
                 (<span class="org-keyword">let-alist</span> o
                   <span class="org-highlight-quoted-quote">`</span>((label . ,.label)
                     (start . ,(- .start threshold-seconds))
                     (end . ,(- .end threshold-seconds))))))
             (copy-sequence sacha-vad-tutor-events)))
           (recent-total (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">+</span> (mapcar (<span class="org-keyword">lambda</span> (o)
                                             (-
                                              (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> o)
                                              (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">start</span> o)))
                                           recent))))
      (<span class="org-keyword">save-excursion</span>
        (insert
         (format <span class="org-string">"Last %d minute%s:\n"</span>
                 (/ threshold 60)
                 (<span class="org-keyword">if</span> (&gt; (/ threshold 60) 1)
                     <span class="org-string">"s"</span>
                   <span class="org-string">""</span>))
         (mapconcat
          (<span class="org-keyword">lambda</span> (o)
            (format <span class="org-string">"%d%% %s\n"</span>
                    (/ (* 100.0 (cdr o)) recent-total)
                    (car o)))
          (seq-map
           (<span class="org-keyword">lambda</span> (group)
             (cons (car group)
                   (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">+</span> (mapcar (<span class="org-keyword">lambda</span> (o)
                                       (-
                                        (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> o)
                                        (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">start</span> o)))
                                     (cdr group)))))
           (seq-group-by (<span class="org-keyword">lambda</span> (o) (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> o)) recent)))
         <span class="org-string">"\n"</span>)
        (svg-insert-image
         (sacha-vad-visualize
          sacha-vad-tutor-labels
          recent
          t
          width height))
        (insert
         <span class="org-string">"\nTotal:\n"</span>

         (mapconcat
          (<span class="org-keyword">lambda</span> (o)
            (format <span class="org-string">"%d%% %s\n"</span>
                    (/ (* 100.0 (cdr o)) total)
                    (car o)))
          sacha-vad-tutor-summary))
        (svg-insert-image
         (sacha-vad-visualize sacha-vad-tutor-labels sacha-vad-tutor-events t
                              width height
                              sacha-vad-tutor-session-length))
        (insert <span class="org-string">"\n\n"</span>  (sacha-learn-lang-bingo-summary))))))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-handle-end-event</span> (data)
  <span class="org-doc">"Handle the end event."</span>
  (<span class="org-keyword">let</span> ((event (list
                (cons <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> data))
                (cons <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">start</span> (- (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">timestamp</span> data)
                                (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">duration</span> data)
                                (<span class="org-keyword">or</span> sacha-vad-tutor-start-seconds 0)))
                (cons <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> (- (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">timestamp</span> data)
                              (<span class="org-keyword">or</span> sacha-vad-tutor-start-seconds 0))))))
    (<span class="org-keyword">push</span> event sacha-vad-tutor-events)
    (<span class="org-keyword">let-alist</span> data
      (<span class="org-keyword">incf</span> (alist-get (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> data) sacha-vad-tutor-summary 0 nil <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">string=</span>)
            .duration))
    (sacha-vad-tutor-update-view)))

(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-event-handler-functions</span> <span class="org-highlight-quoted-quote">'</span>(sacha-vad-speech-process) <span class="org-doc">"Functions to call with the VAD/transcription events."</span>)

(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-process-my-speech</span> nil <span class="org-doc">"Non-nil means process my speech through the whisper setup."</span>)
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-speech-process</span> (data)
  <span class="org-doc">"Process DATA through my whisper input process if it's my speech.</span>
<span class="org-doc">Add to `</span><span class="org-doc"><span class="org-constant">sacha-vad-event-handler-functions</span></span><span class="org-doc">'."</span>
  (<span class="org-keyword">when</span> (<span class="org-keyword">and</span> sacha-vad-process-my-speech
             (string= (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">event</span> data) <span class="org-string">"final"</span>)
             (string= (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> data) sacha-vad-my-label))
    (<span class="org-keyword">with-current-buffer</span> (get-buffer-create whisper&#45;&#45;stdout-buffer-name)
      (erase-buffer)
      (insert (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">text</span> data))
      (goto-char (point-min)))
    (whisper&#45;&#45;handle-transcription-output nil (get-buffer-create whisper&#45;&#45;stdout-buffer-name))))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-filter</span> (proc string)
  <span class="org-doc">"Add and parse."</span>
  (<span class="org-keyword">when</span> (buffer-live-p (process-buffer proc))
    <span class="org-comment-delimiter">;; </span><span class="org-comment">split the string by newline?</span>
    (<span class="org-keyword">let</span> ((data (<span class="org-keyword">with-current-buffer</span> (process-buffer proc)
                  (goto-char (point-max))
                  (<span class="org-keyword">save-excursion</span>
                    (insert string))
                  (<span class="org-keyword">catch</span> <span class="org-highlight-quoted-quote">'</span><span class="org-constant">waiting</span>
                    (<span class="org-keyword">unless</span> (<span class="org-keyword">or</span> (eobp) (looking-at <span class="org-string">"\n*$"</span>))
                      (json-read))))))
      (run-hook-with-args <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">sacha-vad-event-handler-functions</span> data)
      (<span class="org-keyword">when</span> (string= (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">event</span> data) <span class="org-string">"end"</span>)
        (sacha-vad-handle-end-event data))
      (<span class="org-keyword">when</span> (string= (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">event</span> data) <span class="org-string">"final"</span>)
        (sacha-vad-handle-text-event data)))))

(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-last-speaker</span> nil <span class="org-doc">"Previous speaker."</span>)

(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-transcript-buffer</span> nil <span class="org-doc">"If non-nil, add speech to this buffer."</span>)
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-add-to-transcript</span> (text)
  <span class="org-doc">"Add TEXT to the transcript buffer."</span>
  (<span class="org-keyword">when</span> sacha-vad-transcript-buffer
    (<span class="org-keyword">with-current-buffer</span> (get-buffer-create sacha-vad-transcript-buffer)
      (set (make-local-variable <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">window-point-insertion-type</span>) t)
      (<span class="org-keyword">save-excursion</span>
        (goto-char (point-max))
        (insert text)))))

(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-my-label</span> <span class="org-string">"sacha"</span>)
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-handle-text-event</span> (data)
  (sacha-vad-add-to-transcript
   (concat
    (<span class="org-keyword">if</span> (string= sacha-vad-last-speaker (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> data))
        <span class="org-string">" "</span>
      (concat (<span class="org-keyword">if</span> (bobp) <span class="org-string">""</span> <span class="org-string">"\n\n"</span>) (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> data) <span class="org-string">": "</span>))
    (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">text</span> data)))
  (<span class="org-keyword">setq</span> sacha-vad-last-speaker (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> data))
  (<span class="org-keyword">when</span> (string= (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> data) sacha-vad-my-label)
    (sacha-learn-lang-notice-bingo-words (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">text</span> data))))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-start</span> ()
  <span class="org-doc">"Start the process."</span>
  (<span class="org-keyword">interactive</span>)
  (sacha-vad-tutor-ensure)
  (<span class="org-keyword">setq</span> sacha-vad-tutor-summary <span class="org-highlight-quoted-quote">'</span>((<span class="org-string">"sacha"</span> . 0)
                                  (<span class="org-string">"other"</span> . 0))
        sacha-vad-tutor-events nil)
  (<span class="org-keyword">with-current-buffer</span> (get-buffer-create <span class="org-string">"*Transcript*"</span>)
    (erase-buffer)
    (display-buffer (current-buffer)))
  (display-buffer (get-buffer-create <span class="org-string">"*tutor stats*"</span>)))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-ensure</span> ()
  <span class="org-doc">"Start the process if it's not already running."</span>
  (<span class="org-keyword">interactive</span>)
  (<span class="org-keyword">unless</span> (process-live-p sacha-vad-tutor-process)
    (<span class="org-keyword">setq</span> sacha-vad-tutor-process-start-time (current-time))
    (<span class="org-keyword">setq</span> sacha-vad-tutor-start-seconds 0)
    (<span class="org-keyword">let</span> ((default-directory sacha-vad-tutor-dir))
      (<span class="org-keyword">setq</span> sacha-vad-tutor-process
            (make-process
             <span class="org-builtin">:name</span> <span class="org-string">"vad-tutor"</span>
             <span class="org-builtin">:command</span> sacha-vad-tutor-command
             <span class="org-builtin">:connection-type</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">pipe</span>
             <span class="org-builtin">:buffer</span> (get-buffer-create <span class="org-string">"*vad-tutor*"</span>)
             <span class="org-builtin">:stderr</span> (get-buffer-create <span class="org-string">"*vad-tutor-err*"</span>)
             <span class="org-builtin">:filter</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">sacha-vad-tutor-filter</span>)))))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-stop</span> ()
  <span class="org-doc">"Stop the process."</span>
  (<span class="org-keyword">interactive</span>)
  (interrupt-process sacha-vad-tutor-process))

<span class="org-comment-delimiter">;;;</span><span class="org-comment">###</span><span class="org-comment"><span class="org-warning">autoload</span></span>
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-js-parse-jsonl-file</span> (file)
  <span class="org-doc">"Parse jsonl as a list"</span>
  (<span class="org-keyword">let</span> (results)
    (<span class="org-keyword">with-temp-buffer</span>
      (insert-file-contents file)
      (goto-char (point-min))
      (<span class="org-keyword">while</span> (not (eobp))
        (<span class="org-keyword">condition-case</span> nil
            (<span class="org-keyword">push</span> (json-read) results)
          (<span class="org-warning">error</span> (goto-char (point-max))))))
    (nreverse results)))

(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-start-seconds</span> 0 <span class="org-doc">"Number of seconds to consider as the start."</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-vad-tutor-process-start-time</span> nil <span class="org-doc">"Time that the process started."</span>)

<span class="org-comment-delimiter">;;;</span><span class="org-comment">###</span><span class="org-comment"><span class="org-warning">autoload</span></span>
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-reset-clock</span> ()
  <span class="org-doc">"Reset the start of the session."</span>
  (<span class="org-keyword">interactive</span>)
  (<span class="org-keyword">setq</span> sacha-vad-tutor-start-seconds
        (time-to-seconds (time-subtract (current-time)
                                        sacha-vad-tutor-process-start-time)))
  (mapcar (<span class="org-keyword">lambda</span> (o) (setcdr o 0)) sacha-vad-tutor-summary)
  (<span class="org-keyword">setq</span> sacha-vad-tutor-events nil))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-visualize</span> (labels list <span class="org-type">&amp;optional</span> output-file width height max-seconds)
  (<span class="org-keyword">setq</span> height (<span class="org-keyword">or</span> height 25))
  (<span class="org-keyword">setq</span> width (<span class="org-keyword">or</span> width (window-width nil t)))
  (<span class="org-keyword">let*</span> ((svg (svg-create width height))
         (max-seconds (max
                       (<span class="org-keyword">or</span> max-seconds 0)
                       (<span class="org-keyword">if</span> list
                           (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">max</span> (mapcar (<span class="org-keyword">lambda</span> (o) (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> o))
                                               list))
                         0)))
         (pixels-per-second (/ (* 1.0 width) max-seconds))
         (row-height (/ height (length labels))))
    (<span class="org-keyword">dolist</span> (o list)
      (<span class="org-keyword">let-alist</span> o
        (<span class="org-keyword">let</span> ((y (* row-height (<span class="org-keyword">or</span> (seq-position labels .label <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">string=</span>) -1))))
          (<span class="org-keyword">when</span> (&gt;= y 0)
            (svg-rectangle
             svg
             (* .start pixels-per-second)
             y
             (* (- .end .start) pixels-per-second)
             row-height <span class="org-builtin">:fill</span> <span class="org-string">"green"</span>)))))
    (<span class="org-keyword">cond</span>
     ((stringp output-file)
      (<span class="org-keyword">with-temp-file</span> output-file
        (svg-print svg)))
     (output-file
      svg)
     (t
      (<span class="org-keyword">with-current-buffer</span> (get-buffer-create <span class="org-string">"*vad*"</span>)
        (goto-char (point-min))
        (display-buffer (current-buffer))
        (erase-buffer)
        (svg-insert-image svg))))))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-stats</span> (labels list)
  <span class="org-doc">"Return the balance of time talking as (label total percentage)."</span>
  (<span class="org-keyword">let*</span> ((max-seconds (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">+</span> (mapcar
                                 (<span class="org-keyword">lambda</span> (o) (<span class="org-keyword">let-alist</span> o (- .end .start)))
                                 list)))
         (durations (mapcar (<span class="org-keyword">lambda</span> (o)
                            (cons
                             (car o)
                             (mapcar (<span class="org-keyword">lambda</span> (e) (<span class="org-keyword">let-alist</span> e (- .end .start)))
                                     (cdr o))))
                          (seq-group-by (<span class="org-keyword">lambda</span> (o) (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">label</span> o))
                                        list))))
    (seq-map
     (<span class="org-keyword">lambda</span> (label)
       (<span class="org-keyword">let*</span> ((row (cdr (assoc label durations)))
              (total (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">+</span> row)))
         <span class="org-highlight-quoted-quote">`</span>((label . ,label)
           (total . ,total)
           (count . ,(length row))
           (percentage . ,(<span class="org-keyword">and</span> total max-seconds (&gt; max-seconds 0) (* 100.0 (/ total max-seconds)))))))
     labels)))

<span class="org-comment-delimiter">;; </span><span class="org-comment">(sacha-vad-stats '("sacha" "tutor") (seq-take (sacha-js-parse-jsonl-file "~/proj/speech/output2.log") 5))</span>
<span class="org-comment-delimiter">;; </span><span class="org-comment">(sacha-vad-stats '("sacha" "tutor") (sacha-js-parse-jsonl-file "~/proj/speech/output.log"))</span>
<span class="org-comment-delimiter">;; </span><span class="org-comment">(sacha-vad-visualize '("sacha" "tutor") (sacha-js-parse-jsonl-file "~/proj/speech/output.log"))</span>
<span class="org-comment-delimiter">;; </span><span class="org-comment">(sacha-vad-visualize '("sacha" "tutor") (seq-take (sacha-js-parse-jsonl-file "~/proj/speech/output2.log") 30))</span>

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-group-output</span> (files)
  (<span class="org-keyword">let</span> ((grouped-files (seq-group-by
                        (<span class="org-keyword">lambda</span> (o)
                          (substring (file-name-base o) 0 10))
                        files)))
    (mapcar
     (<span class="org-keyword">lambda</span> (group)
       (<span class="org-keyword">let</span> ((start 0))
         (cons (car (cdr group))
               (seq-reduce
                (<span class="org-keyword">lambda</span> (prev file)
                  (append prev
                          (<span class="org-keyword">let</span> ((max-seconds 0))
                            (<span class="org-keyword">prog1</span> (mapcar (<span class="org-keyword">lambda</span> (event)
                                             (<span class="org-keyword">incf</span> (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">start</span> event) start)
                                             (<span class="org-keyword">incf</span> (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> event) start)
                                             (<span class="org-keyword">setq</span> max-seconds (max (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> event) max-seconds))
                                             event)
                                           (sacha-js-parse-jsonl-file file))
                              (<span class="org-keyword">setq</span> start max-seconds)))))
                (cdr group)
                nil))))
     grouped-files)))

(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-vad-tutor-make-table</span> (files <span class="org-type">&amp;optional</span> width height)
  <span class="org-doc">"Make a summary table."</span>
  (<span class="org-keyword">let</span> ((labels sacha-vad-tutor-labels)
        (width (<span class="org-keyword">or</span> width 500))
        (height (<span class="org-keyword">or</span> height 30)))
    <span class="org-comment-delimiter">;; </span><span class="org-comment">combine data as needed</span>
    (append
     <span class="org-highlight-quoted-quote">'</span>((<span class="org-string">"Date"</span> <span class="org-string">"Tutor"</span> <span class="org-string">"Minutes"</span> <span class="org-string">"%"</span> <span class="org-string">"Visualization"</span>) hline)
     (seq-map
      (<span class="org-keyword">lambda</span> (group)
        (<span class="org-keyword">let*</span> ((list (cdr group))
               (base (file-name-base (car group))))
          (<span class="org-keyword">if</span> list
              (<span class="org-keyword">let-alist</span> (car (sacha-vad-stats labels list))
                (<span class="org-keyword">let</span> ((svg-name
                       (expand-file-name
                        (concat (file-name-base (car group)) <span class="org-string">"-"</span> .label <span class="org-string">".svg"</span>)
                        <span class="org-string">"~/proj/french/conversation"</span>)))
                  (sacha-vad-visualize
                   (list .label)
                   list
                   svg-name
                   width
                   height)
                  (list
                   (format-time-string <span class="org-string">"%Y-%m-%d"</span>
                                       (seconds-to-time
                                        (sacha-filename-timestamp (car group))))
                   <span class="org-comment-delimiter">;; </span><span class="org-comment">(org-link-make-string (concat "file:" o) "file")</span>
                   (<span class="org-keyword">if</span> (string-match <span class="org-string">"-conversation-</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">[</span><span class="org-string"><span class="org-negation-char">^</span></span><span class="org-string">\\.]+</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">"</span> base)
                       (s-capitalize (substring (match-string 1 base) 0 1))
                     <span class="org-string">""</span>)

                   (round (/ .total 60))
                   (format <span class="org-string">"%d%%"</span> .percentage)
                   (org-link-make-string (concat <span class="org-string">"file:"</span> svg-name)
                                         (concat <span class="org-string">"file:"</span> svg-name))
                   )))
            (list <span class="org-string">"?"</span>))))
      (sacha-vad-tutor-group-output files))
     nil)))
</code></pre>
</div>

</details>
</div>
</div>
<div id="outline-container-writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-noticing-bingo-phrases" class="outline-3">
<h3 id="writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-noticing-bingo-phrases"><span class="section-number-3">2.</span> <a href="https://sachachua.com/blog/feed/index.xml#writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-noticing-bingo-phrases">Noticing bingo phrases</a></h3>
<div class="outline-text-3" id="text-writing-and-editing-speech-recognition-using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time-noticing-bingo-phrases">
<p>
Here's the code that reminds me to use the words or expressions on my
to-do list, and then keeps track of how often I've used them,
prioritizes the ones that I haven't used yet, and displays these.
It can work with either my Whisper dictation workflow or the VAD
events server described above.
</p>

<p>
I keep the list of expressions in <a href="https://sachachua.com/blog/2026/10/using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time/bingo.txt">file:///home/sacha/proj/french/bingo.txt</a>.
</p>

<details><summary>Code to notice when I've used an expression</summary>
<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-learn-lang-bingo-word-file</span> <span class="org-string">"~/proj/french/bingo.txt"</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-learn-lang-bingo-words</span> nil <span class="org-doc">"List of words."</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">sacha-learn-lang-bingo-words-hash</span> nil <span class="org-doc">"Data for the counts."</span>)

<span class="org-comment-delimiter">;;;</span><span class="org-comment">###</span><span class="org-comment"><span class="org-warning">autoload</span></span>
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-learn-lang-bingo-words</span> ()
  <span class="org-doc">"Load the list of words from the file."</span>
  (<span class="org-keyword">interactive</span>)
  (<span class="org-keyword">setq</span> sacha-learn-lang-bingo-words
        (seq-map (<span class="org-keyword">lambda</span> (o)
                   (<span class="org-keyword">when</span> (= (length o) 1)
                     (<span class="org-keyword">setq</span> o (list (car o) 0)))
                   (<span class="org-keyword">setq</span> o (append o (list
                                      (string-trim (replace-regexp-in-string
                                                    <span class="org-string">" *\\[.+?\\] *"</span> <span class="org-string">" "</span>
                                                    (car o))))))
                   o)
                 (pcsv-parse-file sacha-learn-lang-bingo-word-file)))
  sacha-learn-lang-bingo-words)

<span class="org-comment-delimiter">;;;</span><span class="org-comment">###</span><span class="org-comment"><span class="org-warning">autoload</span></span>
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-learn-lang-bingo-handler</span> (event)
  <span class="org-doc">"Manually register the event."</span>
  (<span class="org-keyword">interactive</span> <span class="org-string">"e"</span>)
    (<span class="org-keyword">let*</span> ((pos (posn-point (event-end event)))
           (val (get-text-property pos <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">bingo</span>)))
      (<span class="org-keyword">when</span> val
        (sacha-learn-lang-notice-bingo-words val))))

(<span class="org-keyword">defvar-keymap</span> sacha-learn-lang-bingo-map
  <span class="org-string">"&lt;mouse-1&gt;"</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">sacha-learn-lang-bingo-handler</span>)

<span class="org-comment-delimiter">;;;</span><span class="org-comment">###</span><span class="org-comment"><span class="org-warning">autoload</span></span>
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-learn-lang-bingo-summary</span> ()
  <span class="org-doc">"Summarize the words to say."</span>
  (<span class="org-keyword">let</span> ((target (seq-filter (<span class="org-keyword">lambda</span> (row) (= (elt row 1) 0))
                            sacha-learn-lang-bingo-words))
        (yay (seq-filter (<span class="org-keyword">lambda</span> (row) (&gt; (elt row 1) 0))
                         sacha-learn-lang-bingo-words)))
    (concat
     (<span class="org-keyword">if</span> target
         (concat <span class="org-string">"Target: "</span>
                 (mapconcat (<span class="org-keyword">lambda</span> (row)
                              (propertize
                               (car row)
                               <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">keymap</span> sacha-learn-lang-bingo-map
                               <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">bingo</span> (car row)))
                            target
                            <span class="org-string">"; "</span>)
                 <span class="org-string">"\n\n"</span>)
       <span class="org-string">""</span>)
     (<span class="org-keyword">if</span> yay
         (concat
          <span class="org-string">"Yay! "</span>
          (mapconcat (<span class="org-keyword">lambda</span> (row)
                       (propertize (format <span class="org-string">"%s (%s)"</span> (car row) (elt row 1))
                                   <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">keymap</span> sacha-learn-lang-bingo-map
                                   <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">bingo</span> (car row)))
                     (seq-filter (<span class="org-keyword">lambda</span> (row) (&gt; (elt row 1) 0))
                                 sacha-learn-lang-bingo-words)
                     <span class="org-string">"; "</span>)
          <span class="org-string">"\n\n"</span>)
       <span class="org-string">""</span>))))

<span class="org-comment-delimiter">;;;</span><span class="org-comment">###</span><span class="org-comment"><span class="org-warning">autoload</span></span>
(<span class="org-keyword">defun</span> <span class="org-function-name">sacha-learn-lang-notice-bingo-words</span> (text)
  <span class="org-doc">"Look for the bingo words in TEXT and celebrate when I use them.</span>
<span class="org-doc">Add to `</span><span class="org-doc"><span class="org-constant">whisper-insert-text-at-point</span></span><span class="org-doc">'."</span>
  (<span class="org-keyword">when</span> text
    (<span class="org-keyword">unless</span> sacha-learn-lang-bingo-words (sacha-learn-lang-bingo-words))
    (<span class="org-keyword">let</span> ((found (seq-find
                  (<span class="org-keyword">lambda</span> (o)
                    (<span class="org-keyword">and</span> (elt o 2)
                         (string-match (concat <span class="org-string">"\\&lt;"</span> (elt o 2)) text)))
                  sacha-learn-lang-bingo-words)))
      (<span class="org-keyword">when</span> found
        (<span class="org-keyword">incf</span> (elt found 1))
        (sacha-vad-tutor-update-view)
        (<span class="org-keyword">with-current-buffer</span> (get-buffer-create <span class="org-string">"*Bingo*"</span>)
          (erase-buffer)
          (insert (sacha-learn-lang-bingo-summary))))))
  text)
</code></pre>
</div>

</details>
</div>
</div>

<div class="note">This is part of my <a href="https://sachachua.com/dotemacs#writing-and-editing-speech-recognition-reporting-speaking-stats-balance">EmacsConfiguration.</a></div><div><a href="https://sachachua.com/blog/2026/10/using-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F10%2Fusing-emacs-to-visualize-speaking-and-target-words-phrases-in-real-ish-time%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Categorizing Emacs News items by voice in Org Mode</title>
		<link>https://sachachua.com/blog/2026/03/categorizing-emacs-news-items-by-voice-in-org-mode/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Tue, 24 Mar 2026 01:19:29 GMT</pubDate>
    <category>speech</category>
<category>speech-recognition</category>
<category>emacs</category>
<category>org</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2026/03/categorizing-emacs-news-items-by-voice-in-org-mode/</guid>
		<description><![CDATA[<p>
I'm having fun exploring which things might actually be easier to do by voice than by typing. For example, after I wrote some code to <a href="https://sachachua.com/dotemacs#writing-and-editing-speech-recognition-expanding-yasnippet-by-voice">expand yasnippets by voice</a>, I realized that it was easier to:
</p>

<ol class="org-ol">
<li>press my shortcut,</li>
<li>say "okay, define interactive function",</li>
<li>and then press my shortcut again,</li>
</ol>

<p>
than to:
</p>

<ol class="org-ol">
<li>mentally say it,</li>
<li>get the first initials,</li>
<li>type in "dfi",</li>
<li>and press Tab to expand.</li>
</ol>

<p>
Another area where I do this kind of mental translation for keyboard shortcuts is when I categorize dozens of Emacs-related links each week for <a href="https://sachachua.com/blog/category/emacs-news">Emacs News</a>. I used to do this by hand. Then I wrote a function to try to guess the category based on regular expressions (<code>my-emacs-news-guess-category</code> in <a href="https://raw.githubusercontent.com/sachac/emacs-news/refs/heads/master/index.org">emacs-news/index.org</a>, which is large). Then I set up a menu that lets me <a href="https://sachachua.com/blog/2019/06/making-a-numpad-based-hydra-for-categorizing-org-list-items/">press numbers corresponding to the most frequent categories</a> and use tab completion for the rest. 1 is Emacs Lisp, 2 is Emacs development, 3 is Emacs configuration, 4 is appearance, 5 is navigation, and so on. It's not very efficient, but some of it has at least gotten into muscle memory, which is also part of why it's hard to change the mapping. I don't come across that many links for Emacs development or Spacemacs, and I could probably change them to something else, but&hellip; Anyway.
</p>


<figure id="org36cd391">
<a href="https://sachachua.com/blog/2026/03/categorizing-emacs-news-items-by-voice-in-org-mode/2026-03-23_20-38-33.png"><img src="https://sachachua.com/blog/2026/03/categorizing-emacs-news-items-by-voice-in-org-mode/2026-03-23_20-38-33.png" alt="2026-03-23_20-38-33.png"></a>

<figcaption><span class="figure-number">Figure 1: </span>Screenshot of my menu for categorizing links</figcaption>
</figure>

<p>
I wanted to see if I could categorize links by voice instead. I might not always be able to count on being able to type a lot, and it's always fun to experiment with other modes of input. Here's a demonstration showing how Emacs can automatically open the URLs, wait for voice input, and categorize the links using a reasonably close match. The <code>*Messages*</code> buffer displays the recognized output to help with debugging.
</p>

<div class="media-post" id="org09d244a">
<p>
</p><figure><video controls="1" src="https://sachachua.com/blog/2026/03/categorizing-emacs-news-items-by-voice-in-org-mode/2026-03-23-compressed.webm" type="video/webm"><a href="https://sachachua.com/blog/2026/03/categorizing-emacs-news-items-by-voice-in-org-mode/2026-03-23-compressed.webm">Download the video</a></video><figcaption><div>Screencast with audio: categorizing links by voice</div></figcaption></figure>
<p></p>

</div>

<p>
This is how it works:
</p>

<ol class="org-ol">
<li>It starts an <code>ffmpeg</code> recording process.</li>
<li>It starts <a href="https://github.com/snakers4/silero-vad">Silero voice activity detection</a>.</li>
<li>When it detects that speech has ended, it use <code>curl</code> to send the WAV to an OpenAI-compatible server (in my case, <a href="https://speaches.ai/">Speaches</a> with the <code>Systran/faster-whisper-base.en</code> model) for transcription, along with a prompt to try to influence the recognition.</li>
<li>It compares the result with the candidates using <code>string-distance</code> for an approximate match. It calls the code to move the current item to the right category, creating the category if needed.</li>
</ol>

<p>
Since this doesn't always result in the right match, I added an Undo command. I also have a Delete command for removing the current item, Scroll Up and Scroll Down, and a way to quit.
</p>
<div id="outline-container-categorizing-emacs-news-items-by-voice-in-org-mode-initial-thoughts" class="outline-3">
<h3 id="categorizing-emacs-news-items-by-voice-in-org-mode-initial-thoughts">Initial thoughts</h3>
<div class="outline-text-3" id="text-categorizing-emacs-news-items-by-voice-in-org-mode-initial-thoughts">
<p>
I used it to categorize lots of links in this week's Emacs News, and I think it's promising. I loved the way my hands didn't have to hover over the number keys or move between those and the characters. Using voice activity detection meant that I could just keep dictating categories instead of pressing keyboard shortcuts or using the foot pedal I recently dusted off. There's a slight delay, of course, but I think it's worth it. If this settles down and becomes a solid part of my workflow, I might even be able to knit or hand-sew while doing this step, or simply do some stretching exercises.
</p>

<p>
<b>What about using streaming speech recognition?</b> I've written some code to <a href="https://sachachua.com/dotemacs#writing-and-editing-speech-recognition-streaming-speech-recognition-into-emacs-using-google-chrome-web-speech-api">use streaming speech recognition</a>, but the performance wasn't good enough when I tried it on my laptop (Lenovo P52 released in 2018, no configured GPU under Linux). The streaming server dropped audio segments in order to try to catch up. I'd rather have everything transcribed at the level of the model I want, even if I have to wait a little while. I also tried <a href="https://sachachua.com/dotemacs/index.html#writing-and-editing-speech-recognition-streaming-speech-recognition-into-emacs-using-google-chrome-web-speech-api">using the Web Speech API in Google Chrome for real-time speech transcription</a>, but it's a little finicky. I'm happy with the performance I get from either <a href="https://sachachua.com/blog/2026/01/queue-multiple-transcriptions-with-whisper-el-speech-recognition/">manually queueing speech segments</a> or <a href="https://sachachua.com/blog/2026/01/using-silero-voice-activity-detection-to-automatically-queue-multiple-transcriptions-with-natrys-whisper-el/">using VAD</a> and then using batch speech recognition with a model that's kept in memory (which is why I use a local server instead of a command-line tool). Come to think of it, I should try this with a higher-quality model like medium or large, just in case the latency turns out to be not that much more for this use case.
</p>

<p>
<b>What about external voice control systems</b> like <a href="https://talonvoice.com/">Talon Voice</a> or <a href="https://www.cursorless.org/">Cursorless</a>? They seem like neat ideas and lots of people use them. I think hacking something into Emacs with full access to its internals could be lots of fun too.
</p>

<p>
A <i>lot</i> of people have experimented with voice input for Emacs over the years. It could be fun to pick up ideas for commands and grammars. Some examples:
</p>

<ul class="org-ul">
<li><a href="https://www.youtube.com/watch?v=8SkdfdXWYaI">Using Python to Code by Voice - YouTube</a> (2013)</li>
<li><a href="https://github.com/jgarvin/mandimus">jgarvin/mandimus: Use speech recognition to command your computer and Emacs. · GitHub</a></li>
<li><a href="https://github.com/ErikPrantare/cursorfree.el">ErikPrantare/cursorfree.el: Edit and navigate from anywhere in the buffer · GitHub</a></li>
<li><a href="https://git.sr.ht/~lepisma/emacs-speech-input/tree/master/item/README.org">~lepisma/emacs-speech-input</a> - uses the idea of a voice cursor, uses an LLM to execute editing instructions</li>
</ul>

<p>
<b>What about automating myself out of this loop?</b> I've considered training a classifier or sending the list to a large language model to categorize links in order to set more reasonable defaults, but I think I'd still want manual control, since the fun is in getting a sense of all the cool things that people are tinkering around with in the Emacs community. I found that with voice control, it was easier for me to say the category than to look for the category it suggested and then say "Okay" to accept the default. If I display the suggested category in a buffer with very large text (and possibly category-specific background colours), then I can quickly glance at it or use my peripheral vision. But yeah, it's probably easier to look at a page and say "Org Mode" than to look at the page, look at the default text, see if it matches Org Mode, and then say okay if it is.
</p>
</div>
</div>
<div id="outline-container-categorizing-emacs-news-items-by-voice-in-org-mode-ideas-for-next-steps" class="outline-3">
<h3 id="categorizing-emacs-news-items-by-voice-in-org-mode-ideas-for-next-steps">Ideas for next steps</h3>
<div class="outline-text-3" id="text-categorizing-emacs-news-items-by-voice-in-org-mode-ideas-for-next-steps">
<p>
I wonder how to line up several categories. I could probably rattle off a few without waiting for the next one to load, and just pause when I'm not sure. Maybe while there's a reasonably good match within the first 1-3 words, I'll take candidates from the front of the queue. Or I could delimit it with another easily-recognized word, like "next".
</p>

<p>
I want to make a more synchronous version of this idea so that I can have a speech-enabled drop-in replacement that I can use as my <code>y-or-n-p</code> while still being able to type <code>y</code> or <code>n</code>. This probably involves using <code>sit-for</code> and polling to see if it's done. And then I can use that to play Twenty Questions, but also to do more serious stuff. It would also be nice to have replacements for <code>read-string</code> and <code>completing-read</code>, since those block Emacs until the user enters something.
</p>

<p>
I might take a side-trip into a conversational interface for M-x doctor and M-x dunnet, because why not. Naturally, it also makes sense to voice-enable <a href="https://github.com/xenodium/agent-shell">agent-shell</a> and <a href="https://github.com/karthink/gptel">gptel</a> interactions.
</p>

<p>
I'd like to figure out a number- or word-based completion mechanism so that I can control Reddit link replacement as well, since I want to select from a list of links from the page. Maybe something similar to the way <a href="https://github.com/jcaw/voicemacs">voicemacs adds numbers to helm and company</a> or how <a href="http://www.cb1.com/~john/computing/emacs/handsfree/flexi-choose.html">flexi-choose.el</a> works.
</p>

<p>
I'm also thinking about how I can shift seamlessly between typing and speaking, like when I want to edit a link title. Maybe I can check if I'm in the minibuffer and what kind of minibuffer I'm in, perhaps like the way <a href="https://github.com/oantolin/embark">Embark</a> does.
</p>

<p>
It would be really cool to define speech commands by reusing the keymap structure that menus also use. This is how to <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Easy-Menu.html">define a menu</a> in Emacs Lisp:
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">easy-menu-define</span> words-menu global-map
  <span class="org-doc">"Menu for word navigation commands."</span>
  <span class="org-highlight-quoted-quote">'</span>(<span class="org-string">"Words"</span>
     [<span class="org-string">"Forward word"</span> forward-word]
     [<span class="org-string">"Backward word"</span> backward-word]))
</code></pre>
</div>


<p>
and this is how to <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Modifying-Menus.html">set just one binding</a>:
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(keymap-set-after my-menu <span class="org-string">"&lt;drink&gt;"</span>
  <span class="org-highlight-quoted-quote">'</span>(<span class="org-string">"Drink"</span> . drink-command) <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">eat</span>)
</code></pre>
</div>


<p>
That makes sense to reuse for speech commands. I'd also like to be able to specify aliases while hiding them or collapsing them for a "What can I say" help view&hellip; Also, if keymaps work, then maybe minor modes or transient maps could work? This sort of feels like it should be the voice equivalent of a transient map.
</p>
</div>
</div>
<div id="outline-container-categorizing-emacs-news-items-by-voice-in-org-mode-the-code-so-far" class="outline-3">
<h3 id="categorizing-emacs-news-items-by-voice-in-org-mode-the-code-so-far">The code so far</h3>
<div class="outline-text-3" id="text-categorizing-emacs-news-items-by-voice-in-org-mode-the-code-so-far">

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">my-emacs-news-categorize-with-voice</span> (<span class="org-type">&amp;optional</span> skip-browse)
  (<span class="org-keyword">interactive</span> (list current-prefix-arg))
  (<span class="org-keyword">unless</span> skip-browse
    (my-spookfox-browse))
  (speech-input-cancel-recording)
  (<span class="org-keyword">let</span> ((default (<span class="org-keyword">if</span> (fboundp <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">my-emacs-news-guess-category</span>) (my-emacs-news-guess-category))))
    (speech-input-from-list
     (<span class="org-keyword">if</span> default
         (format <span class="org-string">"Category (%s): "</span> default)
       <span class="org-string">"Category: "</span>)
     <span class="org-highlight-quoted-quote">'</span>((<span class="org-string">"Org Mode"</span> <span class="org-string">"Org"</span> <span class="org-string">"Org Mode"</span>)
       <span class="org-string">"Other"</span>
       <span class="org-string">"Emacs Lisp"</span>
       <span class="org-string">"Coding"</span>
       (<span class="org-string">"Emacs configuration"</span> <span class="org-string">"Config"</span> <span class="org-string">"Configuration"</span>)
       (<span class="org-string">"Appearance"</span> <span class="org-string">"Appearance"</span>)
       (<span class="org-string">"Default"</span> <span class="org-string">"Okay"</span> <span class="org-string">"Default"</span>)
       <span class="org-string">"Community"</span>
       <span class="org-string">"AI"</span>
       <span class="org-string">"Writing"</span>
       (<span class="org-string">"Reddit"</span> <span class="org-string">"Read it"</span> <span class="org-string">"Reddit"</span>)
       <span class="org-string">"Shells"</span>
       <span class="org-string">"Navigation"</span>
       <span class="org-string">"Fun"</span>
       (<span class="org-string">"Dired"</span> <span class="org-string">"Directory"</span> <span class="org-string">"Dir ed"</span>)
       (<span class="org-string">"Mail, news, and chat"</span> <span class="org-string">"News"</span> <span class="org-string">"Mail"</span> <span class="org-string">"Chat"</span>)
       <span class="org-string">"Multimedia"</span>
       <span class="org-string">"Scroll down"</span>
       <span class="org-string">"Scroll up"</span>
       <span class="org-string">"Web"</span>
       <span class="org-string">"Delete"</span>
       <span class="org-string">"Skip"</span>
       <span class="org-string">"Undo"</span>
       (<span class="org-string">"Quit"</span> <span class="org-string">"Quit"</span> <span class="org-string">"Cancel"</span> <span class="org-string">"All done"</span>))
     (<span class="org-keyword">lambda</span> (result text)
       (message <span class="org-string">"Recognized %s original %s"</span> result text)
       (<span class="org-keyword">pcase</span> result
         (<span class="org-string">"Undo"</span>
          (undo)
          (my-emacs-news-categorize-with-voice t))
         (<span class="org-string">"Skip"</span>
          (forward-line)
          (my-emacs-news-categorize-with-voice))
         (<span class="org-string">"Quit"</span>
          (message <span class="org-string">"All done."</span>)
          (speech-input-cancel-recording))
         (<span class="org-string">"Reddit"</span>
          (my-emacs-news-replace-reddit-link)
          (my-emacs-news-categorize-with-voice t))
         (<span class="org-string">"Scroll down"</span>
          (my-spookfox-scroll-down)
          (my-emacs-news-categorize-with-voice t))
         (<span class="org-string">"Scroll up"</span>
          (my-spookfox-scroll-up)
          (my-emacs-news-categorize-with-voice t))
         (<span class="org-string">"Delete"</span>
          (delete-line)
          (undo-boundary)
          (my-emacs-news-categorize-with-voice))
         (<span class="org-string">"Default"</span>
          (my-org-move-current-item-to-category
           (concat default <span class="org-string">":"</span>))
          (undo-boundary)
          (my-emacs-news-categorize-with-voice))
         (_
          (my-org-move-current-item-to-category
           (concat result <span class="org-string">":"</span>))
          (undo-boundary)
          (my-emacs-news-categorize-with-voice))))
     t)))
</code></pre>
</div>


<p>
It uses <a href="https://sachachua.com/dotemacs#spookfox">Spookfox to control Firefox from Emacs</a>:
</p>

<p>

</p><div class="org-src-container">
<pre class="src src-emacs-lisp" id="org22d90d7"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">my-spookfox-scroll-down</span> ()
  (<span class="org-keyword">interactive</span>)
  (spookfox-js-injection-eval-in-active-tab <span class="org-string">"window.scrollBy(0, document.documentElement.clientHeight);"</span> t))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-spookfox-scroll-up</span> ()
  (<span class="org-keyword">interactive</span>)
  (spookfox-js-injection-eval-in-active-tab <span class="org-string">"window.scrollBy(0, -document.documentElement.clientHeight);"</span>))
</code></pre>
</div>


<p></p>

<p>

</p><div class="org-src-container">
<pre class="src src-emacs-lisp" id="orga82aa0d"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">my-spookfox-background-tab</span> (url <span class="org-type">&amp;rest</span> args)
  <span class="org-doc">"Open URL as a background tab."</span>
  (<span class="org-keyword">if</span> spookfox&#45;&#45;connected-clients
      (spookfox-tabs&#45;&#45;request (cl-first spookfox&#45;&#45;connected-clients) <span class="org-string">"OPEN_TAB"</span> <span class="org-highlight-quoted-quote">`</span>(<span class="org-builtin">:url</span> ,url))
    (browse-url url)))
</code></pre>
</div>


<p></p>

<p>
It also uses these functions for <a href="https://sachachua.com/dotemacs#digital-index-piles-with-emacs">categorizing Org Mode items</a>:
</p>

<p>

</p><div class="org-src-container">
<pre class="src src-emacs-lisp" id="org955687d"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">my-org-move-current-item-to-category</span> (category)
    <span class="org-doc">"Move current list item under CATEGORY earlier in the list.</span>
<span class="org-doc">  CATEGORY can be a string or a list of the form (text indent regexp).</span>
<span class="org-doc">  Point should be on the next line to process, even if a new category</span>
<span class="org-doc">  has been inserted."</span>
    (<span class="org-keyword">interactive</span> (list (completing-read <span class="org-string">"Category: "</span> (my-org-get-list-categories))))
    (<span class="org-keyword">when</span> category
      (<span class="org-keyword">let*</span> ((col (current-column))
             (item (point-at-bol))
             (struct (org-list-struct))
             (category-text (<span class="org-keyword">if</span> (stringp category) category (elt category 0)))
             (category-indent (<span class="org-keyword">if</span> (stringp category) 2 (+ 2 (elt category 1))))
             (category-regexp (<span class="org-keyword">if</span> (stringp category) category (elt category 2)))
             (end (elt (car (last struct)) 6))
             (pos (point))
             s)
        (<span class="org-keyword">setq</span> s (org-remove-indentation (buffer-substring-no-properties item (org-list-get-item-end item struct))))
        (<span class="org-keyword">save-excursion</span>
          (<span class="org-keyword">if</span> (string= category-text <span class="org-string">"x"</span>)
              (org-list-send-item item <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">delete</span> struct)
            (goto-char (caar struct))
            (<span class="org-keyword">if</span> (re-search-forward (concat <span class="org-string">"^ *- +"</span> category-regexp) end t)
                (<span class="org-keyword">progn</span>
                  <span class="org-comment-delimiter">;; </span><span class="org-comment">needs a patch to ol.el to check if stringp</span>
                  (org-list-send-item item (point-at-bol) struct)
                  (org-move-item-down)
                  (org-indent-item))
              (goto-char end)
              (org-list-insert-item
               (point-at-bol)
               struct (org-list-prevs-alist struct))
              (<span class="org-keyword">let</span> ((old-struct (copy-tree struct)))
                (org-list-set-ind (point-at-bol) struct 0)
                (org-list-struct-fix-bul struct (org-list-prevs-alist struct))
                (org-list-struct-apply-struct struct old-struct))
              (goto-char (point-at-eol))
              (insert category-text)
              (org-list-send-item item <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">end</span> struct)
              (org-indent-item)
              (org-indent-item))
            (recenter))))))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-org-guess-list-category</span> (<span class="org-type">&amp;optional</span> categories)
  (<span class="org-keyword">interactive</span>)
  (<span class="org-keyword">require</span> <span class="org-highlight-quoted-quote">'</span><span class="org-constant">cl-lib</span>)
  (<span class="org-keyword">unless</span> categories
    (<span class="org-keyword">setq</span> categories
          (my-helm-org-list-categories-init-candidates)))
  (<span class="org-keyword">let*</span> ((beg (line-beginning-position))
         (end (line-end-position))
         (string (buffer-substring-no-properties beg end))
         (found
          (cl-member string
                     categories
                     <span class="org-builtin">:test</span>
                     (<span class="org-keyword">lambda</span> (string cat-entry)
                       (<span class="org-keyword">unless</span> (string= (car cat-entry) <span class="org-string">"x"</span>)
                         (string-match (regexp-quote (downcase (car cat-entry)))
                                       string))))))
    (<span class="org-keyword">when</span> (car found)
      (my-org-move-current-item-to-category
       (cdr (car found)))
      t)))
</code></pre>
</div>


<p></p>

<p>
For the <code>speech-input</code> functions, experimental code is at <a href="https://codeberg.org/sachac/speech-input">https://codeberg.org/sachac/speech-input</a> .
</p>
</div>
</div>
<div><a href="https://sachachua.com/blog/2026/03/categorizing-emacs-news-items-by-voice-in-org-mode/index.org">View Org source for this post</a></div>
<p>You can <a href="https://social.sachachua.com/@sacha/statuses/01KMER8FH7NZT56ENT0NHE36Q0" target="_blank" rel="noopener noreferrer">comment on Mastodon</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F03%2Fcategorizing-emacs-news-items-by-voice-in-org-mode%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Expanding yasnippets by voice in Emacs and other applications</title>
		<link>https://sachachua.com/blog/2026/03/expanding-yasnippet-by-voice/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Wed, 04 Mar 2026 16:17:40 GMT</pubDate>
    <category>emacs</category>
<category>audio</category>
<category>speech-recognition</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2026/03/expanding-yasnippet-by-voice/</guid>
		<description><![CDATA[<p>
<a href="https://github.com/joaotavora/yasnippet">Yasnippet</a> is a template system for Emacs. I want to use it by voice. I'd like to be able to say things like "Okay, define interactive function" and have that expand to a matching snippet in Emacs or other applications. Here's a quick demonstration of expanding simple snippets:
</p>

<div class="media-post" id="org7591a42">
<p>
</p><figure><video controls="1" src="https://sachachua.com/blog/2026/03/expanding-yasnippet-by-voice/2026-03-04_12.33.19.webm" type="video/webm"><track kind="subtitles" label="Captions" src="https://sachachua.com/blog/2026/03/expanding-yasnippet-by-voice/2026-03-04_12.33.19.vtt" srclang="en" default=""><a href="https://sachachua.com/blog/2026/03/expanding-yasnippet-by-voice/2026-03-04_12.33.19.webm">Download the video</a></video><figcaption><div>Screencast of expanding snippets by voice in Emacs and in other applications</div></figcaption></figure>
<p></p>

<details class="code-details" style="padding: 1em;
                 border-radius: 15px;
                 font-size: 0.9em;
                 box-shadow: 0.05em 0.1em 5px 0.01em  #00000057;">
                  <summary><strong>Transcript</strong></summary>
<ul class="org-ul">
<li><span class="media-time" data-start="0.000">00:00</span> So I've defined some yasnippets with names that I can say. Here, for example, in this menu, you can see I've got "define interactive function" and "with a buffer that I'll display." And in fundamental mode, I have some other things too. Let's give it a try.</li>
<li><span class="media-time" data-start="19.000">00:19</span> I press my shortcut. "Okay, define an interactive function." You can see that this is a yasnippet. Tab navigation still works.</li>
<li><span class="media-time" data-start="33.000">00:33</span> I can say, "OK, with a buffer that I'll display," and it expands that also.</li>
<li><span class="media-time" data-start="45.000">00:45</span> I can expand snippets in other applications as well, thanks to a global keyboard shortcut.</li>
<li><span class="media-time" data-start="50.000">00:50</span> Here, for example, I can say, "OK, my email." It inserts my email address.</li>
<li><span class="media-time" data-start="62.000">01:02</span> Yasnippet definitions can also execute Emacs Lisp. So I can say, "OK, date today," and have that evaluated to the actual date.</li>
<li><span class="media-time" data-start="81.000">01:21</span> So that's an example of using voice to expand snippets.</li>
</ul>


</details>

</div>

<p>
This is handled by the following code:
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">my-whisper-maybe-expand-snippet</span> (text)
  <span class="org-doc">"Add to `</span><span class="org-doc"><span class="org-constant">whisper-insert-text-at-point</span></span><span class="org-doc">'."</span>
  (<span class="org-keyword">if</span> (<span class="org-keyword">and</span> text
           (string-match
            <span class="org-string">"^ok</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(?:</span></span><span class="org-string">ay</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">?[,\\.]? </span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">.+</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">"</span> text))
    (<span class="org-keyword">let*</span> ((name
            (downcase
             (string-trim
              (replace-regexp-in-string <span class="org-string">"[,\\.]"</span> <span class="org-string">""</span> (match-string 1 text)))))
           (matching
            (seq-find (<span class="org-keyword">lambda</span> (o)
                        (subed-word-data-compare-normalized-string-distance
                         name
                         (downcase (yas&#45;&#45;template-name o))))
                      (yas&#45;&#45;all-templates (yas&#45;&#45;get-snippet-tables)))))
      (<span class="org-keyword">if</span> matching
          (<span class="org-keyword">progn</span>
            (<span class="org-keyword">if</span> (frame-focus-state)
                (<span class="org-keyword">progn</span>
                  (yas-expand-snippet matching)
                  nil)
              <span class="org-comment-delimiter">;; </span><span class="org-comment">In another application</span>
              (<span class="org-keyword">with-temp-buffer</span>
                (yas-minor-mode)
                (yas-expand-snippet matching)
                (buffer-string))))
        text))
    text))
</code></pre>
</div>


<p>
This code relies on <a href="https://github.com/sachac/whisper.el/">my fork of whisper.el</a>, which lets me specify a list of functions for <code>whisper-insert-text-at-point</code>. (I haven't asked for upstream review yet because I'm still testing things, and I don't know if it actually works for anyone else yet.) It does approximate matching on the snippet name using a function from <a href="https://github.com/sachac/subed/blob/main/subed/subed-word-data.el">subed-word-data.el</a> which just uses <code>string-distance</code>. I could probably duplicate the function in my config, but then I'd have to update it in two places if I come up with more ideas.
</p>

<p>
The code for inserting into other functions is defined in <a href="https://sachachua.com/blog/2026/01/using-whisper-el-to-capture-text-to-speech-in-emacs/#multimedia-whisper">my-whisper-maybe-type</a>, which is very simple:
</p>

<p>

</p><div class="org-src-container">
<pre class="src src-emacs-lisp" id="org3c4a075"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">my-whisper-maybe-type</span> (text)
  <span class="org-doc">"If Emacs is not the focused app, simulate typing TEXT.</span>
<span class="org-doc">Add this function to `</span><span class="org-doc"><span class="org-constant">whisper-insert-text-at-point</span></span><span class="org-doc">'."</span>
  (<span class="org-keyword">when</span> text
    (<span class="org-keyword">if</span> (frame-focus-state)
        text
      (make-process <span class="org-builtin">:name</span> <span class="org-string">"xdotool"</span> <span class="org-builtin">:command</span>
                    (list <span class="org-string">"xdotool"</span> <span class="org-string">"type"</span>
                          text))
      nil)))
</code></pre>
</div>


<p></p>

<p>
Someday I'd like to provide alternative names for snippets. I also want to make it easy to fill in snippet fields by voice. I'd love to be able to answer minibuffer questions from <code>yas-choose-value</code>, <code>yas-completing-read</code>, and other functions by voice too. Could be fun!
</p>

<p>
Related:
</p>

<ul class="org-ul">
<li><a href="https://github.com/sachac/snippets">My snippets repository</a></li>
<li><a href="https://sachachua.com/topic/speech-recognition/">My topic page for speech recognition</a></li>
</ul>

<div class="note">This is part of my <a href="https://sachachua.com/dotemacs#writing-and-editing-speech-recognition-expanding-yasnippet-by-voice">Emacs configuration.</a></div><div><a href="https://sachachua.com/blog/2026/03/expanding-yasnippet-by-voice/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F03%2Fexpanding-yasnippet-by-voice%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item>
	</channel>
</rss>