Larynx - A viable Linux TTS

I spent some time over the weekend experimenting with voice2json and rhasspy, trying to set up a fully offline voice assistant system using Mozilla DeepSpeech for speech recognition, a template file containing all known phrases and their mappings to intents, an intent recognizer and a local shell script to parse the recognized intent and invoke commands (think opening a website or folder when an intent is recognized). Rhasspy was easy to use and really fun. It’s amazing how far we’ve come in terms of open-source tools in the TTS/speech recognition space.

Along the way, I discovered Larynx, a TTS system for Linux with high-quality voices from Glow-TTS and others, with intonations that sound human. I’ve kept an eye on the Linux TTS space for years and have been disappointed by the limited consumer-use options. Often, the pre-trained TTS voices sound all too robotic for everyday use. I suppose that’s understandable given the dearth of open-source voice datasets (which is why projects like Mozilla CommonVoice are so exciting!). It’s nice to have a pleasant pre-trained TTS model natively available on Linux.

My use case is to copy text in a browser/Thunderbird RSS article, hit a shortcut and have the TTS system read the selected text aloud so I can look away from the screen and just listen.


I followed the Debian installation instructions, and downloaded and installed the tts, lang-en_us and Harvard Glow TTS files.

# cd Downloads # (or /path/to/downloaded/deb/files)
sudo apt install ./larynx*.deb

TTS Shortcut

To create a shortcut that invokes Larynx on selected text, I added aliases in my ~/.bash_aliases file. They use xclip to access clipboard and selection data. On Debian-based systems, you should be able to install it with sudo apt install xclip.

# Speak text passed as argument
# Usage: speak "This is a test"
alias speak="larynx --voice harvard-glow_tts --interactive"

# Speak clipboard text
# Usage: speak-clipboard
alias speak-clipboard="xclip -out -selection clipboard | speak"

# Speak currently selected text
# Usage: speak-selection
alias speak-selection="xclip -out -selection primary | speak"

Under Settings –> Keyboard on GNOME, I added a custom keybinding for Super+S to invoke bash -i -c "speak-selection". This lets me select any text and hit Super+S to invoke larynx


Jodi Sudoku

Happy to share that I’m working on Jodi Sudoku, an open-source (AGPLv3), Sudoku progressive web app with the goal of implementing multiplayer support using WebRTC!

I’ve been following the adoption of WebRTC (now supported on all major platforms except Safari, infamous for dragging its feet) and the decentralized web for years, but haven’t had the opportunity to work with it, so I decided to use it in a hobby project.


The frontend code uses the standard React + Typescript setup, with React Router and Redux. So far, the web app features

  • Starting a new game with a level of difficulty of your choice
  • Keyboard and mouse/touch input
    • Changing the mode of entering values into cells - Choose a digit first, and then click a cell to enter the digit, or vice versa
  • Responsive cross-platform layout, tested on Firefox and Chrome (desktop and mobile)
  • Undo and redo using redux store history

I’m a big fan of internationalization and supporting regional languages, so the basic one-player implementation currently supports Kannada, English and Polish using react-i18n.


The backend is more esoteric - I’m interested in using Rails + WebSockets. While I primarily wear the Android hat at work, our backend is built with Rails (a framework I had not worked with before) and I’m interested in improving my Rails fu through this project and contributing more than just the occasional pull request. Rails has a thin wrapper over WebSockets called Action Cable, and I’m curious to see how it fares compared to Node ( +, which seems to be the internet’s go-to recommendation for scalable WebSockets. WebSockets is a great way to achieve peer discovery (your browser tab discovering peers and finding a way to establish a direct connection despite NAT traversal), required for WebRTC. If there’s a lot of traffic and Action Cable/Rails struggles under the load, I’m aware I might need to swap out Action Cable for Node someday, but I think the Rails experimentation will be worth it. Besides, Node feels like home turf and it’ll be fun to try something different :)


What’s with the idea of multiplayer Sudoku, you ask? Is there any interest in multiplayer Sudoku? I’m not sure - I found a few multiplayer games on different app stores, but I don’t think there’s a big community. This project is honestly just an excuse to find a way to play Sudoku with my mum - Playing Sudoku together is a ritual whenever I’m in Bangalore. With mugs of basil and ginger tea after dinner, and armed with pens at the kitchen table, we pair up (Jodi (ಜೋಡಿ) is the Kannada word for “pair”) on solving the Sudoku puzzle in the daily newspaper (remember those?), trying to get as many digits as possible but also explaining to each other how we “unlocked” the right digits. I miss that, and I see no reason why that should stop when I’m not in Bangalore :) I’d be happy if others find it useful as well.

The idea is to implement URL-based discovery of peers. Like Jitsi calls, users who open the same link will be able to connect to the server using WebSockets, receive information from the server on the users in the “room”, and then establish browser-to-browser WebRTC peer connections, making the server connection theoretically unnecessary after that point.

I’m thinking of building different multiplayer modes

  • Cooperative: all players in a room collaboratively solve the same puzzle together
  • Challenge (same puzzle): all players in a room individually solve the same puzzle, only aware of the number of empty/filled cells of other players.
  • Challenge (puzzle of same difficulty): All players in a room solve different puzzles of the same difficulty level, only aware of the number of empty/filled cells of other players. Could be fun to also add a “Peek player’s board” feature
  • Time challenge? Turn-based games? Some dastardly variant of Sudoku?

Q: Privacy and fault tolerance

I’ve encountered a lot of flaky internet connections and I’d like Jodi Sudoku to be tolerant of and handle edge cases related to flaky connections, users having to refresh their tabs, abrupt drop-offs etc. Given that exploring WebRTC and peer-to-peer systems is one of the goals, it’s imperative that the server know as little as possible about users, rooms or the type and state of the game in a room.

If the server knows nothing about the state of the game in a room, who is the source of truth in case of failure? Should all players in a room store the game state locally (in the browser local/session storage), and send the most recent state (via WebRTC) to any player who re/joins a room? What happens if two players disagree on the most recent state of the game? Should rooms have a leader who is responsible for storing the game state and sharing it with users who enter the room? What happens if the leader’s connection goes down? I have struggled to square the two seemingly contradictory goals of privacy/decentralization, and maintaining a source of truth for fault recovery, and decided to move forward with making the server aware of users, rooms, and game states. Please reach out if you have any suggestions that would keep the server out of the loop after peer discovery.

My current approach is:

Peer discovery

  1. Abbi enters a room (a room is identified by the link, i.e,
  2. Abbi establishes a WebSocket connection with the server.
  3. Abbi receives the list of users in the room via WebSocket. The list does not have anyone except Abbi. The WebSocket connection with the server is kept open.
  4. Ilana enters the room, connects to the server and receives the list of users. The WebSocket connection with the server is kept open.
  5. Abbi and Ilana use a STUN server, bypass their NAT, and establish a peer-to-peer connection.


  1. Abbi and Ilana begin a cooperative game, notifying each other via WebRTC and notifying the server via WebSocket.
  2. Abbi and Ilana send messages directly to each other via WebRTC when they enter values into cells. They would also need to passively notify the server of the state changes via WebSocket
  3. The server passively stores the game state, doing nothing with it.

New player/rejoin

  1. Jaime joins the game late, and is announced to the other users by the server (the updated list of users is shared) via WebSocket
  2. The server shares the latest state of the game to Jaime via WebSocket
  3. Once connected to the other users via WebRTC p2p connections, Jaime follows step 7 to participate in the game.

Using the server as the single source of truth allows users who join a game midway or experience connection issues and rejoin the game to continue the (collaborative) game from the latest state (if other users have made changes). Other multiplayer modes may need to handle failure/rejoin scenarios differently.

Next steps

I realise I’ve defined a lot of lofty technical ambitions for a simple Sudoku webapp, and I’d like to take this one step at a time. I’m planning to make small releases to keep up my motivation and get feedback from my target demographic of one - When it comes to hobby projects, I tend to be very excited during the design phase and in the early days of implementation, and lose interest and move on to the next shiny puzzle that needs solving when the problem is more or less “solved” in my head :)

The next release will have peer discovery and cooperative game mode.

European fantasy

For the first time in ten years, I marked one whole year (and counting) living in the same country thanks to Covid, and while I’ve enjoyed liberties in Kenya (open restaurants, only one major lockdown and few restrictions, few cases until recently) that many around the world are still waiting to get back, I find myself longing to escape to Europe.

Of course, the Europe in my mind is completely untouched by Covid, there are no restrictions, all art spaces are active, you can drink a beer by the river, it’s springtime and sunny, and you can travel as you please, because that’s how I last saw it :)

Trigger warning: Privileged person whining about the hardships of others

The relentlessness of Kenyan inequality can be hard to bear at times - I’ve been trying to explore the streets of Kisumu by bike, venturing into neighbourhoods and streets I don’t usually take. Cycling by the lake wearing my $30 helmet and seeing parts of people’s houses flooded after heavy rains, and kids collecting water from filthy ditches or the lake in distinct yellow jerry cans while someone else washes their motorbike a few metres away makes me want to give up.

You’d think India would’ve inured me to the injustices of poverty and poor infrastructure, but I’ve realised India is quite good at ghettoising its poor, politely tucking them away in long-forgotten parts of towns and cities that are avoided by anyone who can afford to do so, certainly politicians. Or perhaps it’s more accurate to say the wealthy have managed to outspend, gentrify and shut out the poor from “their” neighbourhoods. The segregation seems less stark to me here in Kenya.

A few days after I moved into my apartment, someone rang the doorbell - It was a shy young woman, probably in her late teens. She seemed very uncomfortable and struggled to say, “I would be grateful to you if I could have any work to do in your house - cleaning the floor, and doing your dishes and laundry?”.

What was I to say? It looks like it’s common to have housekeepers in Kenya, but it makes me very uncomfortable, not just because of the risks of catching Covid and the dubious ethics of paying someone to clean up after me, but also I get nervous at the very thought of strangers coming into my private space and having nowhere to hide while they’re around, possibly for hours (The joys of living with anxiety!). With a lot of guilt and fully aware that I might be cutting off an opportunity for her to make a few hundred Kenyan shillings a week, I declined.

Such moral conundrums are an everyday occurrence - Last month, I stepped out to get some bananas from a nearby supermarket, and as I walked past an empty plot, I saw a woman sitting at a small folding table with no shade, selling eggs, fried meat and bananas. A child played in the dirt nearby. The sun was setting after a hot day, and the bananas were well on their way to brown. I felt torn - I had stepped out just to get bananas, and I could support the woman and her child by buying bananas from her knowing that the bananas would go bad within a day, or go to the supermarket and buy less ripe ones. I kept walking (with only a tiny bit of hyperventilating), but it was a tough choice. Everyday, very real tests of Kant’s categorial imperative take their toll. All the bananas I’ve bought since then have been from her though (and the nearly brown bananas must have just been due to a bad day - the bananas have been great since then)! :)

Is it possible to respect your own values and preferences and still find a way to help others that doesn’t involve paying for things or services you don’t need, or simply handing people a wad of cash?

I know it’s pure escapism, but I’m craving a respite from guilt for a bit. It would be a dream to be in a place where there might still be plenty of inequality but not the kind that leads to extreme starvation or deprives people of basic infrastructure or access to healthcare. Where the crunch of a snail on a bike path getting crushed under my bike wheels despite my best efforts to avoid it is the only source of guilt pangs. Where I’m not constantly aware of my privilege.

I never thought I’d think fondly of places where, among many xenophobic incidents, someone called me a terrorist, pointed at my blinking red bike light and made the gesture of a bomb blast :D

An organisation next door stopped burning garbage!

I’ve written about the pollution caused by poor waste management and garbage burning before. That was spurred by an organisation I live near that burnt all kinds of stuff (dry leaves, carton boxes, and sometimes industrial waste I’m sure) everyday! Garbage burning in a nearby compound I was sick of having to keep my windows shut all the time, so I called them one day and reminded them that it was illegal to burn garbage according to NEMA regulations. The person I spoke to claimed ignorance of any garbage burning within their compound, wanted to know how I knew of their (non-existent) burning, was I perhaps mistaken in thinking it was their compound burning garbage and not the next one, and eventually wanted to give me someone else’s phone number so I could talk to them about this issue. I refused, saying it wasn’t any of my business to talk to someone else, and that I just wanted to remind them that they shouldn’t be exposing everyone around them to fumes everyday. We exchanged phone numbers, and that was it. I wasn’t expecting much from the call, but…it worked! They have since stopped burning garbage. I’m not sure what’s happening to all of it now (I sincerely hope they’re using a garbage service that recycles and/or uses an incinerator and that I haven’t just demonstrated NIMBY behaviour), but there’s one fewer compound I have to worry about as a source of smoke.

I’m surprised that all it took was a phone call! Had I known that before, I wouldn’t have spent weeks psyching myself up to call them, arming myself with facts and preparing responses to imagined questions, not to mention how to put pressure on them (social media? legal notice?) should the call fail to do anything :) Now that I know it can be as simple as making a phone call, I feel unleashed, looking for other opportunities to get organisations, as a start, in Kisumu to do better!

That said, I was somewhat ashamed of myself at one point during the call - The person on the phone wanted to know my phone number (I was passed through to them via the receptionist), and the first thought that came to my mind was “Am I putting myself in danger by sharing my phone number?”. Thanks to mpesa, it’s easy to look up anyone’s full name if you have their Kenyan phone number. Would the organisation find it easier or more cost-effective to shut me up than to deal with the daily garbage burning? I went ahead and gave them my phone number, but I had to remind myself that if it comes to it, I have the resources to deal with any problems the organisation might try to create for me. Made me think of environmentalists who risk their lives to wage year-long campaigns against mining giants and illegal loggers - If a simple thing like calling someone out on garbage burning can make me feel at risk of harm (it’s hard to say if that was paranoia or a legitimate threat), the folks fighting the real battles must be constantly aware of the danger they’re in and yet find a way to put their fears aside and continue to do the work they do ♥

Hey Kisumu, clean up your air!

Kisumu is undergoing a major transformation - Preparing to host the Africities summit in November 2021 April 2022, the county government has been setting up public infrastructure, building footpaths (with rumours of bike paths! 😍), the construction of a new market complex (although the demolition of open-air markets before the launch of the new market has not gone down well with small-scale traders already hit hard by COVID) and even the revival of a colonial-era railway line. I’m excited to see what the city will look like later this year!

The air pollution problem

Kisumu’s air quality has gone completely unnoticed in this effort to “upgrade” the city. As I write this, Kisumu’s air quality index is hovering at 99, considered poor in most parts of the world. I wish I had known when I was moving into an apartment building that with the nice views of the hills in the distance, I’d also have to deal with the noxious fumes of burning garbage from nearby compounds burning dry leaves, or a few days’ worth of food, plastic and other waste. Running to the windows and closing them shut at the first whiff of burning garbage has become an unfortunate necessity. I’ve noticed I wake up with a sore throat or stuffy nose if I leave the bedroom window open all night.

Burning is the predominant form of waste management in Kenya, and notwithstanding National Environment Management Authority’s claim that burning of waste must be undertaken in a licenced incinerator, dumping garbage in a heap (either in a designated corner within one’s own compound or in a public field) and setting fire to it is a common sight in the evenings (Not wanting to breathe in the fumes is my current excuse for giving up on running in the evenings). It’s not much better using a garbage collection service - The building uses one, and chatting with the garbage collector, I found out that the collected garbage does not undergo segregation of any kind and all of it is burnt (I have no reason to think he meant incinerated and not just burnt in an open field on the outskirts of town).

At a time when WHO estimates that 7 million people die every year due to air pollution, of which 1 million are from the African continent, it doesn’t seem to me like there’s a national conversation on air pollution. The adverse effects of ambient air pollution are well established, and I think Kisumu has a great opportunity to address the pollution issue as the city continues to grow.


According to the City of Kisumu’s own Kisumu Integrated Solid Waste Management Plan (KISWaMP) strategy document (PDF), garbage collection rates are abysmal - In a city that produces 385 tons of waste every day, only 25% is effectively collected. The figures are worse for household waste - Only 3.1% of household waste is collected for garbage disposal. While the strategy document defines lofty goals for 2020 and 2030 including planning ahead for the growth of the city and decommissioning the Kachok dumpsite, its implementation is far from reality.

Here’s an idea

With a little financial help from international partners, I think Kisumu county could set up its own free citywide garbage collection service. On its own, or with the help of community based organisations and nonprofits, the county administration can widen access to proper garbage disposal to all parts of the city. The strategy document reveals willingness to use a disposal service and to learn segregation methods, which can be taught through community outreach programs.

Given that 65 - 70% of waste in Kisumu is estimated to be decomposable, organic materials, composting would significantly reduce the volume of garbage to be processed and transported to incinerators or landfills.


  • Clean air, water and soil: Improvement in the air quality, and reduction in the pollutants in the water (due to open dumping in the lake) and leachates in the soil would lead to better health outcomes and a lower healthcare expenditure burden on the county government over time.
  • Employment: 41% of people aged between 18 and 34 in Kisumu were unemployed, according to the 2019 census data. That was before COVID, so I can’t imagine how dire the situation must be nowadays (A few months ago, I saw about forty people queueing outside an office to apply for a security guard position!). Setting up a new waste management program, or supporting community based organisations in various estates/settlements of Kisumu could create hundreds of jobs!
  • Fertilizer: Compost is a great fertilizer. Composting organic waste and selling the compost to farmers at a nominal rate and conducting programs to increase awareness of compost use would be a self-sustaining source of revenue for the county’s garbage collection service and hopefully lead to better yield for farmers?
  • Aesthetically pleasing: Assuming improved access leads to a reduction in illegal dumping of waste in public fields, trenches and highways, Kisumu would be able to position itself as a clean, progressive city with a long-term vision.