Selected work / Case study

J.A.R.V.I.S.

A desktop voice assistant in Python. Say its name, ask for something and it answers out loud.

Built with
Python, SpeechRecognition, gTTS, pygame, requests
Language model
OpenRouter chat completions API

01

How a request travels

J.A.R.V.I.S. starts as a text prompt in the terminal. Typing listen switches on voice mode, which runs in a background thread so the keyboard keeps working. From then on, every spoken request takes the same path:

  1. Wake wordShort clips go to speech recognition until one contains “Jarvis”
  2. CommandIt answers “Yes?” and records up to five seconds
  3. RouterThe text is matched against the registered commands
  4. Handler or modelA command runs directly. Anything else goes to the model
  5. AnswerThe reply is spoken with gTTS and played through pygame

02

A router you can extend

Commands live in a dictionary that maps a phrase to a handler. Each module in commands/ registers its own phrases at startup, so adding a skill means adding a module and leaves the core untouched.

The process_command method in jarvis.py
The router itself, from jarvis.py.

Matching runs in two passes. First comes an exact match, like clear context. Then comes a prefix match, where the rest of the sentence becomes the argument: “open github.com” finds open and passes github.com to the browser handler. If neither pass matches, the request is treated as conversation.

  • open or browse followed by a site opens it in the default browser
  • listen and stop listening switch voice mode on and off
  • speak reads any text aloud
  • context and clear context show or reset what it remembers

03

Memory that expires

Each exchange is saved as a turn with a timestamp. The assistant keeps the last ten turns and forgets anything older than five minutes. A conversational request goes to the model together with a short system prompt for the J.A.R.V.I.S. persona and the three most recent turns.

That's enough for a follow-up like “why is that?” to make sense. It also keeps every request small, and it stops a topic from an hour ago from leaking into a new question.

04

Not listening to itself

A voice assistant that hears its own answer gets stuck in a loop. After the wake word, J.A.R.V.I.S. says “Yes?” and waits for the playback to finish. It then spends half a second measuring the room's background noise again before it starts recording the command.

The recogniser also adjusts its energy threshold as it runs, and it treats a pause of 0.8 seconds as the end of a phrase. A quiet room and a noisy one both work without changing any settings.

05

Failing out loud, politely

A program you talk to can't show a stack trace. Network errors become spoken sentences that say what went wrong, such as no connection or a timeout. If no command is heard, it says so. The voice loop pauses for a second after an error, so it can't spin in a tight loop.

A missing API key turns off conversation, but the built-in commands keep working.

06

Looking back

If I picked it up again, I'd start here:

  • Listening for the wake word sends short clips to an online recogniser. A small offline wake-word model would answer faster and keep everything on the machine until the user actually speaks to it.
  • Prefix matching is simple but greedy: any sentence that starts with “open” becomes a browser command. Matching on intent, or asking the model to choose a command, would handle more natural phrasing.
  • The model and its reply length are fixed in the code. They belong in settings, so trying a different model doesn't mean editing the source.

Get in touch

I'm looking for an internship for the winter semester 2027/28. Email is the quickest way to reach me.

hsynrsdd@gmail.com