Building a Voice Input Method Without Writing Code — One Ad Guy's Complete Product Thinking
I'm an advertising guy, I basically can't code, and I'd never touched Windows client development. I still shipped a commercial Windows voice input method.
- Breaking down the need: why iFlytek / Celia / Doubao / WeChat / typeless all fell short
- Technology selection is the biggest blind spot for someone who can't program — "I don't know what I don't know"
- The POC stalled on latency → Claude Code taught me streaming ASR → a hands-on comparison of 5 vendors
- A Polish layer to fix homophones (Doubao kept hearing "Claude Code" as "cloud")
- 100 interaction details: device switching, middle mouse button, per-character billing
- I never wrote a single line of code through the whole thing.
"Not knowing how to code isn't an excuse. What's left is always on the 'think it through' side."
In the last few episodes I demoed operating Claude Code by voice, and a crowd of people in the comments kept asking — “where did you buy that voice input method?”
That’s when it hit me. The thing I’m using doesn’t have an equivalent on the market.
So this episode, I’ll walk through how it got built, from the beginning.
I’m an advertising guy. I basically can’t code, and I’d definitely never touched Windows client development. But I still wanted an input method that felt right — fast, accurate, mixed Chinese and English, with hotwords I could manage myself.
Can’t write code? Then let Claude Code be my hands, and I’ll handle thinking the whole product through.
After a stretch of polishing, it went from POC to commercially usable, and friends are already using it.
1. Breaking down the need — why no input method on the market felt right
Before you work out what you want, work out what’s wrong with what already exists.
iFlytek IME — no updates in years, its recognition engine stuck in the second tier. Celia Keyboard — extremely accurate on Huawei HarmonyOS (my guess is it has system-level permissions and gets the context), but HarmonyOS only, no Windows version. Doubao IME — the Windows version is still in development. WeChat’s built-in voice input on PC — actually very good. typeless from overseas — over ten US dollars a month, plus a pile of odd features I’d never use. Shandianshu, domestically — well built, but you can’t manage the hotword library or customization yourself.
After the research, not one of them fully fit me. So either put up with it, or build it.
2. Technology selection — the biggest blind spot for someone who can’t program
“I don’t know what I don’t know” — that’s the biggest blind spot for someone who can’t program.
For the exact same feature, the language and framework underneath can be completely different. Python is easy but the software gets bloated; Rust is tiny and blazing fast but its ecosystem is a steeper climb.
I laid the requirement out for Claude Code explicitly: “This is my use case, these are my requirements. What technology choices are viable — give me a multi-dimensional evaluation and report back.”
The research came back with Rust + Tauri: small binary size, low memory footprint, runs as a single lean file, stable recognition, cross-platform. Write the floating window in HTML/CSS and you can deploy to Mac and Android later.
I picked Rust.
But I have to add a caveat: this doesn’t mean everyone should pick Rust. Python is easier for AI-assisted coding, the community has far more experience, and there’s reference code everywhere. Only when the model is strong enough do you have a shot at squeezing the binary down this small. This route demands a lot from the model.
3. Hands-on ASR engine testing — 5 streaming ASR vendors compared
In the POC stage I first got the whole chain working with Gemini 3 Flash: press a key to record → model recognizes → paste at the cursor position.
But every time I finished speaking I had to wait several seconds before the first character appeared. The entire recording got shipped to Google’s overseas servers and back, which felt terrible, and long recordings tended to cut out.
I asked Claude Code “how do I reduce latency,” and it told me — “use streaming ASR.” Recognize each small segment as you speak it, no waiting for the whole clip. And streaming splits further into local and cloud.
That was the “I don’t know what I don’t know” moment. I had a need, Claude Code did the research, and only then did I find out this option existed.
Next: have Claude Code deploy all 5 streaming ASR vendors on my machine for real testing.
I recorded a mixed Chinese-English clip on my phone and ran it through —
- Local models: characters appear in milliseconds, but technical terms come out so wrong you can’t fix them. Fast but unusable.
- Gemini 2.5/3 Flash: very good results, but not streaming — you wait several seconds.
- Doubao streaming cloud input: first character in milliseconds, cloud streaming, and the error rate stays contained across a full clip.
Then I had Claude Code generate a long audio clip for a stability test — no interruptions at all, and fast.
I went with Doubao.
4. The Polish layer — fixing homophone and character-shape errors
Doubao isn’t problem-free. When I say “Claude Code,” for example, it often hears “cloud.” Homophones, imprecise pronunciation, and technical jargon are the systematic blind spot of streaming ASR.
The industry has a solution for this, called the Polish layer — the moment Doubao emits the text, push it straight to a correction model that fixes the wrong words based on context and meaning.
I tested several models for Polish too. It came down to Qwen Flash versus Claude Haiku.
Haiku is slightly better on quality, but costs more, and cross-border transmission adds latency you can’t ignore.
Qwen Flash — fast, cheap, quality good enough, domestic servers with no cross-border latency. I picked Qwen.
Note, up to this point I haven’t written a single line of code. It’s purely research and decisions, research and decisions.
Polish isn’t magic, it’s prompt engineering. I also designed a flow where, after a set number of characters, Claude runs a fine-grained review pass that pulls out the high-frequency terms and the contexts where recognition failed, then turns them into the fix instructions for the next round of Polish. Hotwords that learn themselves.
5. A hundred unremarkable interaction details
Last comes the interaction. A minimal floating window that follows the cursor and doesn’t steal the screen.
A user reported “it stops recognizing when I switch microphones.” I told Claude Code: a user hit this problem in this scenario, my read is that the tracked device disappears after switching microphones, so it should auto-detect audio device changes and prioritize an external mic. Claude Code changed the Rust code and shipped it.
Bind the middle mouse button — because I often operate the computer with a remote and never touch the keyboard.
Auto-send after recognition — the model already corrects typos, so there’s no reason to manually press Enter again.
Switching the pricing model from monthly to per-character — the backend logs showed heavy users blowing past break-even every month; per-character billing is always profitable.
WeChat QR code login — I registered on the WeChat developer platform and sent Claude Code a screenshot of every step so it could walk me through configuring the callback.
I described each of these small things clearly to Claude Code. It makes the change, I test it.
Wrapping up
In the old days this was unthinkable — an advertising guy who has never touched code, shipping a commercial Windows application, and in Rust of all things.
Now it really is just this: if you can articulate the requirement clearly, you can do it.
Not knowing how to code isn’t an excuse. Claude Code has taken over the execution half. What’s left is always on the “think it through” side.
Source: EP0011_audio.mp3 · ASR model gemini-2.5-pro · full text of the 19-minute original recording
[00:00] Come on, friends, good to see you again. In the last few episodes I showed you how I use my voice to drive Claude Code through research work, making decks, or building products. And the result was a whole pile of people in the comments chasing me, saying this voice input method looks so good, can they use it too. And when I replied that I built the voice input method myself, they were all pretty surprised, asking how exactly I built it and whether they could build one themselves.
[00:21] So today let me walk you through it — as a marketing and advertising guy, right, who basically can’t code, let alone build a piece of Windows software, right. How did I think this through from zero and then, step by step, work alongside Claude Code to finish building a product that’s already commercial. Yes, a lot of my friends are already using this input method of mine. So today I had Claude Code lay out my whole development process, to explain it to you in detail.
[00:50] And the most important part isn’t which prompts I used in here — it’s that you really need to see my overall development thinking clearly. Speaking of the input method, let me show you: building a mixed Chinese-English input method from zero — one person, one month, 52 commits, from POC to V0.6.1. How does an input method like this respond fast and recognize mixed Chinese and English content accurately, and produce an output like that.
[01:16] So you can see that after I finish speaking here, it types it in really fast, and that stretch I just said was mixed Chinese and English — not a single error in it. And this service is already live on the site, you can download it. It’s coded in Rust, and the whole input-method installer is only 8 MB. So from what angle did I start building this input method?
[01:36] First, I use these things across all sorts of platforms. The earliest input method I used was actually iFlytek’s, and it’s a pretty good one, but it honestly hasn’t been updated in years. And its recognition engine, its accuracy, is basically stuck at second-tier level. And lately I’ve been using a Huawei HarmonyOS tablet, and anyone who knows will know that HarmonyOS has the Xiaoyi input method, which is extremely accurate — but it only exists on HarmonyOS, there’s no Windows version. And I did think about why they’d do it that way, and my guess is that to some degree they’ve got system-level permissions, which lets them use the context of what the user is typing to do really precise matching.
[02:18] So the Xiaoyi input method really is great, but it only exists on HarmonyOS. And then there’s the Doubao input method — a lot of friends report that Doubao’s input method works well, but if you open Doubao’s official site, you can see the Windows version isn’t out yet, it’s still in development. And lately I’ve also tried the input method from my old employer, Tencent, and I discovered their input method is actually being built by a few different departments. And one of those departments does the PC WeChat client, and the voice input method built into it is also very good — so you could also just use WeChat’s input method.
[02:47] So given all that, what I did first was run a full round of research with Claude Code. I asked it what the better options for input methods are on the Windows platform right now, and how everyone builds this kind of tech. It ran a first round of research, including typeless from overseas and Shandianshuo domestically — both actually pretty solid products.
[03:08] But none of these products quite matched what I needed. For instance that overseas one, typeless — its membership is $12 a month, which really is too expensive. And it’s got some odd features I’d probably never use. What I need is to get text down very fast and very accurately, right — save me effort, and be accurate. And because I’m often working in certain software where you get mixed Chinese and English, in that case I need it accurate. Domestically, Shandianshuo is actually pretty good too, and I’d recommend you try that product, but it has pain points too. For example you have to connect your own model — you can choose the model, sure, but you can’t manage its hot-word dictionary yourself, and including some of the custom stuff, I feel like it’s just not that convenient to use.
[03:50] And so given all that, I started thinking: since I’m investing the time in this, it’s not going to be a demo I play with by myself, right — I want to make it a product that can actually sell, right. Forget how much money it makes, but at the bare minimum it can’t lose money, right. And in the course of building it, since large models are involved, the more usage there is the higher the cost, so I definitely had to make it multi-tenant — a product where users can log in and top up to use it. So right from the start I needed to think through the experience, the core requirements, and the productization infrastructure underneath it.
[04:22] For instance, I need it to be in a minimal state — no complicated settings, no complicated interface, and mixed Chinese and English output that never stutters and stays accurate. It also needs a way to pay, and it needs to do automatic updates — say I fix a bug here, then all the users get a push saying I need to auto-update, to complete that version upgrade. These are all things you figure out in advance.
[04:47] Next comes what’s called stack selection. Most people doing vibe coding have no awareness of this — it’s the blind spot for people who can’t program, the part where I don’t know what I don’t know. They have no idea that the same feature can be implemented in different development languages, with different frameworks underneath. And at that point I didn’t know either, right. So after I’d clearly told Claude Code what I needed, I said: for this use case, for a requirement like mine, what stack options do I have to choose from, and their pros and cons, including the concrete differences — give me an evaluation report across multiple dimensions.
[05:23] And after that round of research, it told me that with Rust, both the software’s size and its memory footprint are extremely small, it can also run minimally as a single file, and its recognition can run stably. At the same time this way of building is cross-platform, so if I want to do a Mac version or an Android version later, I can write the floating window in HTML and CSS and deploy it quickly across platforms. And it also fits the modern recording-and-injection flow. So once the evaluation was done, I picked Rust as the way to build it.
[05:57] Of course, that doesn’t mean you have to pick an extreme setup like this. Why do I say that? Because look at the languages it lists above — say you use Python. When you’re coding with AI, especially if you can’t get your hands on a model as good as Anthropic Opus 4.7, then picking a common language is probably easier to build in, because there’s a lot of community experience to draw on, and a lot of code other people have already written that you can reference or use directly. It’s only when the model is genuinely very capable that you can compress the software’s size from, look, 200 MB down to the 8 MB I ended up with, right. But that process requires the large model to have stronger capability.
[06:40] So right at the start, I picked the most direct route to get the whole chain running end to end — that’s called a POC. Meaning for my product, I first verify whether it can do what I need. So after I’d picked the language, I first used Gemini 3 Flash to get one path working: press a key, record, model recognizes, then paste at the current cursor position. But once I’d built it, I found that this approach was extremely slow. Why?
[07:10] Every time I finished speaking, I’d wait four seconds for recognition, because it sends my entire recording to Google’s Gemini model for ASR transcription, turning the speech into text. And that process has to go out to an overseas server and come back, then recognize and come back. So the whole process took four seconds before I’d see the first character I’d said. That feels really bad from a user-experience standpoint — you start wondering whether the system is even listening to you. And with longer recordings it would easily drop out. Even though its recognition accuracy was very high.
[07:43] So after that test, I went and asked Claude Code: what ways are there to reduce the latency in my voice input method, so that when I’m putting text out, it comes back fast. And at that point CC — Claude Code — came back with research saying, hey, there actually is a solution: streaming ASR. Meaning instead of recognizing after all the speech is done, you cut the audio into small chunks and recognize continuously. And it also told me that this approach splits into local streaming recognition and cloud streaming recognition, meaning we can use our own computer’s compute to do the work.
[08:18] So it was a lightbulb moment, right — I didn’t know about this before, but because I had the need and had Claude Code research it for me, I discovered these options existed. And once I’d found them, I had it go into the community to find how these models are actually regarded. But it turned out a lot of that reputation is PR copy — they brag that accuracy is high and response is fast, but when you actually test the models it doesn’t hold up.
[08:38] So at that point I recorded a stretch of mixed Chinese-English audio on my phone, and had CC run tests for me. I said: help me — first teach me how to deploy all of these large models on my computer, whether that’s via API key or by creating an environment and downloading the model. Then I want to genuinely test every one of the models we just researched, and see how well they do.
[09:00] And once I’d tested them, what I found was: there’s one local model that can spit out text at millisecond level, basically zero latency, but it constantly gets technical terms wrong — so absurdly wrong that I couldn’t fix it even if I went back over it. So that one’s unusable; fast but poor quality means unusable. And then there are good ones like the Gemini 2.5 Flash or the Gemini 3 Flash preview I just mentioned — their results are excellent, but they’re non-streaming, meaning I have to finish the sentence and wait a few seconds before it hands me the whole text, so that experience is bad too.
[09:31] And in the final evaluation, Doubao’s streaming cloud input turned out to be a really great result. Its first-character response is at millisecond level, it’s a cloud streaming approach, and its error rate across a whole passage is basically held to somewhere around 10% to 5%. So at that point I chose a streaming engine, and basically settled on Doubao as the engine.
[09:55] Right, and look, its completion rate is strong too. And after settling on Doubao I did one more thing — I had Claude Code generate a 60-minute long audio file, to test whether I could stably use Doubao’s streaming input method for recognition. And across those 60 minutes of testing there was no interruption at all, and the text appeared continuously, with a total character count of over 15,000, at a rate of 258 characters per minute, which is already blazingly fast. And the zero errors it reports is because the text I gave it for that long recognition run probably wasn’t that complex, and there was a certain amount of repetition in it, so it scored zero errors in this test — a very good result.
[10:35] The local model, as I said, is fast enough, but its precision isn’t there. So in the end I still picked Doubao as the model. But we can also see that in some complex contexts, Doubao’s model runs into homophones, say, or into imprecise pronunciation — like when I say “Claude Code,” right, it might recognize it as “cloud,” right, as in the cloud in the sky. It’s very easy for homophones to be misrecognized because of imprecise pronunciation. So what do you do then?
[11:03] At that point I had CC research the best practices across the whole industry, and found a solution called Polish. Meaning it runs a second round of correction: once Doubao’s streaming voice output has produced the text, it fires it off fast to a correction model, which uses the context and the meaning of the passage to quickly fix words that might be wrong. And same thing with what I just mentioned, like Qwen, right — “Qwen” or “cloud” being misrecognized, or “period” versus “dot” — everyone’s habits are different, and that’s where you get these systematic blind spots in ASR.
[11:37] For the Polish stage I just mentioned, I tested several models too, and finally made a decision — it came down to a choice between Qwen’s Flash model and Anthropic’s Haiku. And in the end what I found was that although Claude Haiku’s quality is slightly better, its cost is also higher, and there’s still that same problem — because of cross-border transmission, a round trip adds somewhere around 500 milliseconds to a second of latency. So overall, even though its quality is better, the recognition experience still isn’t as good as our homegrown Qwen Flash, and that one’s both faster and cheaper, right. So in the end I picked Qwen for this output verification.
[12:18] Note that up to this point I hadn’t written a single line of code — it was just research and decide, research and decide. And this Polish thing isn’t magic, it’s prompt engineering. When we use one of those voice-recognition meeting recorders, or a voice input method, we notice the system asks us to enter some hot words, right — the words that come up a lot in the scenarios I actually use. So these hot words, plus some in-context learning, including how my hot words form a scenario library so the input method can recognize that there are users of the same type and pull that library in to strengthen their input too.
[12:58] So I designed a flow where, every time I’ve entered about 50 items, the system calls Claude’s model to do a fine-grained review: across those 50 final outputs, which words show up at high frequency, or which are obviously errors that came out of the recognition process. It extracts those hot words, and then generalizes the situations where things were misrecognized in context, as a fix for the prompt engineering the next time it runs Polish. This part is a bit more involved — there’s some engineering logic in here.
[13:34] And the last thing is the interaction style. Let me pull up my little window now — you can all see the whole window is like this. Once the system starts, it launches on boot automatically and hides itself in the tray at the bottom right. And once you click it you can log in via WeChat, and it can show how many characters I’ve said today, and what my speaking pace is — 202 characters per minute. That’s not even fast, because I know people using my input method who hit rates of over 220 characters per minute. You can also see my cumulative character count.
[14:01] And I designed a way to change the hotkey, because most of the time you’d hit alt on the keyboard, or control plus something, that kind of key combo, to trigger the voice input method. But I found that a lot of the time I’m not touching the keyboard at all, or, as I shared in the last few episodes, I’m using a remote to drive the computer from a distance. And in that situation I need something integrated with the mouse, so I bind it to the middle mouse button.
[14:24] And then, with a lot of input methods, after you’re done you can delete some characters or fix some characters, right, and then manually hit Enter. But I found that because my input method’s recognition rate is already accurate, plus the fact that I normally use it inside Claude, and the large model corrects the places where my output is wrong — so I designed a feature where it can auto-send once recognition finishes. And that’s an optional feature too. Same with the payment piece, developing the paid features, which I also gave some thought to, and I’ll get into it further down.
[14:55] So the interface is kept minimal — on the right side of the input box there’s something that can show what’s currently been recognized, and once it’s recognized there’s a quick display and the auto-send. See these little dots, those are all little versions one at a time, starting from 0.5.0 — I did an iteration for every one of those point releases. And including, back when I was testing early on, the API key was stored locally, and now it’s stored in the cloud, with some credential-protection work done, including some improvements to security and robustness, right.
[15:29] Or say some user tells me that while they were using the voice input method, they switched microphones, and then found it couldn’t recognize anything. For small experience issues like that, all I have to do is tell Claude Code: this user hit this problem in this usage scenario, and my read is that it’s probably because after switching microphones the device it had been tracking disappeared, and I think in that situation it should automatically detect the change in audio device and pick a better device — say prioritize an external mic and capture through the external mic — and build the feature that way. All I have to do is describe what I need clearly, and Claude Code will go change the Rust code itself and ship it.
[16:07] And then I did some more updates. Early on I didn’t have this login system, and then later I registered on WeChat’s developer platform, screenshotted everything for Claude Code to look at, and had it teach me how to configure this WeChat QR-code login and the callback flow. That was a fantastic learning experience too. So all of my users currently log in by WeChat QR code.
[16:32] Likewise, the earliest pricing plan was monthly — I set a plan at 19 yuan a month. But when I actually ran the backend logs, I found that some users go over every month; their usage can hit 30 or 40 yuan, the heavy users, the ones using the voice input method to write articles. And then there are users who only do maybe 1,000 or 2,000 characters a day, and those users don’t come close to using up the quota. So the monthly-fee model could actually lose me money. And there’s a point here: the more the user loves it and uses it, the more I lose.
[17:05] So later I switched to a pay-by-character-count model. I had CC calculate it for me — across the current test users, how much compute cost their usage burned — and then, factoring in the compute discounts I’ll be able to get later, I finally landed on a cost of 50 cents per thousand characters, which means there’s always some profit in there for me.
[17:25] So the whole flow now is: press the key, then record, then Doubao does streaming output to show the user that I’ve started recognizing and I’m recognizing pretty well, then it goes through Qwen’s correction system for the final cleanup pass, and only then is it sent out. And a front-end system like that, including the WeChat QR login, the per-character billing, the balance reminders — every feature I thought of was just talked out loud at Claude Code for it to code.
[17:54] So in 23 days, I made 52 code commits. But the time this input method actually cost me was somewhere around a dozen-odd hours, for the whole build. Because the development process is back and forth, and I might be doing other things in between, so the total time was probably nowhere near that long.
[18:11] So you can imagine — a piece of software already this polished, already usable by other people, a product that can already make money, and I built it solo in ten hours, and on top of that using the most cutting-edge Rust framework as the approach. That was completely unthinkable before, right. I also built an official site, built a site so consumers can see my update status, including the different client versions launching in the future.
[18:41] In the past this was unthinkable. Now it really is that as long as you can express what you need clearly, you can do it. Any other questions you want to ask, leave a comment in my comments section, and see you next episode. And if you want to use the input method, you can go download it from my input method’s website. Right now there’s only a Windows version — the Mac version is actually already built and in testing, so stay tuned for that. Bye-bye.