Show HN: VOOG – Moog-style polyphonic synthesizer in Python with tkinter GUI (github.com)

96 points by gpasquero ↗ HN
Body: I built a polyphonic synthesizer in Python with a tkinter GUI styled after the Moog Subsequent 37.

  Features: 3 oscillators, Moog ladder filter (24dB/oct), dual ADSR envelopes, LFO, glide, noise generator, 4 multitimbral channels, 19 presets, rotary
  knob GUI, virtual keyboard with mouse + QWERTY input, and MIDI support.

  No external GUI frameworks — just tkinter, numpy, and sounddevice.

19 comments

[ 2.6 ms ] story [ 38.5 ms ] thread
Damn, that is really, really cool.

Thanks for building this and thanks for sharing.

Really ambitious and really cool, congrats on finishing and sharing!

Getting into the weeds, how are you doing individual voices, ie an an analog synth needs a separate signal path for each note of polyphony with inadvertent and unavoidable interference… which ironically is desirable.

Very cool! I will be playing with this.

The only thing that jumped out to me is a lack of a panic button that stops all sound.

There is a reason that most people do not use interpreted languages, or languages with garbage collection, for audio synthesis and DSP.

It's great that it works, and it may well work 99% of the time. And it may have been a great learning experience/platform, so congrats for that.

But it's important for people to understand why this is generally the wrong toolset for this sort of software development, even when it can be so much fun.

Python and other interpreted languages (Lua excepted, with conditions), and languages like Swift that have GC, cannot ensure non-blocking behavior in the code that need to runs in realtime. You can paper over this with very large audio buffers (which makes the synth feel sluggish) or with crossed fingers (which work a surprising amount of the time). But ultimately you need a language like C/C++/Rust etc. to ensure that your realtime DSP code is actually realtime.

Despite Apple pushing Swift "for everything", even they still acknowledge that you should not write AudioUnit (or any other plugin formats) using Swift.

Meanwhile, have fun with this, which it looks like you already did!

Got a friend who is in the high frequency trading industry and uses both Java and C#. I asked about GC. Turns out you just write code that doesn’t need to GC. Object pools, off-heap memory etc.

It won’t do the absolute fastest tasks in the stack quite as well but supposedly the coding speed and memory management benefits are more important, and there’s no GC so it’s reliable.

This seems to conflate different things.

Interpreted is not a problem from the predictable behaviour point of view. You may get less absolute performance. Though with Python you can do the heavy lifting in numpy etc which are in native code. And this is what is done here, see eg https://github.com/gpasquero/voog/blob/main/synth/dsp/envelo...

Languages that have garbage collection: not going to rehash the standard back-and-forth here, suffice it to say that the devil is in the details.

(comment deleted)
You can get pretty descent results (from past experiments I’ve run) of doing something similar to PyTorch et al, ie, make the compute graph (or in this case, the wiring graph for the synth) in Python then have the real time stuff all inside a compiled extension.
I sort of managed to get it working under 3.10 (and it would probably work considerably further back) but the output was a bit wonky, especially when trying to play multiple notes quickly or simultaneously. I had to patch a couple of things related to type annotations in synth/gui/app.py to make it run without MIDI support.

Overall neat concept. I've thought about playing around with sounddevice myself and the code here offers quite a bit of guidance.

Do you plan to put a license on this? Would you be interested in a PR to make a wheel (installable as an application with uv or pipx) from it? Also, I didn't play around with the patches, but it seems to me like they could be refactored to be data-driven.

Doesn't work at all on my system (kubuntu stable, whatever the stock audio subsystem is now). keys stick down when activated with keyboard, labels on keys disappear once played, vu meter moves but no sound comes out except sporadic beeps.
I have recently come to really like tkinter. It has many good concepts. And I too am using it from Python. That said ...

Oh no ... Not another Python project, that doesn't pin its versions with hashes.

    pip install numpy sounddevice
    pip install mido python-rtmidi
This stuff really shouldn't be done in 2026 any longer.

I mean it's a hobby project, so you are free to do what you want, of course. Just please never do this in a professional environment. This is one reason Python projects catch so much flak from many people. One day it works, next day it doesn't. And surely not 2 years later, when a random person stumbles upon the repository and wants to try things. Please make your projects reproducible. Use pinned versions and lock files containing hashes, so that other people can get the same setup and it doesn't become an "It ran on my machine." project.

> One day it works, next day it doesn't. And surely not 2 years later, when a random person stumbles upon the repository and wants to try things.

I would be very surprised if a project like this were broken by a Numpy or sounddevice update within the next 2 years. sounddevice is too simple (and the code uses it in a localized and very simple way), and Numpy too stable (they're pretty good about semver, and it was 18 years from 1.0 to 2.0.0). Anyway, people qualified to set up Python code locally in "dev mode" following instructions like this, should also be qualified to notice the last-commit dates and do that kind of investigative work. (We also now have installers that can just automatically disregard packages published after a certain date.)

The flip side of this is that having every project pin an exact version increases the chance that different projects needlessly demand different versions. The same version could be hard-linked into multiple environments (even if you aren't brave enough to try to stuff multiple applications into a common "sandbox"), avoiding bloat. And sure, you don't care about a few megs of disk space. But not everyone has a fast Internet connection. And Fastly presumably cares that total PyPI is now in the exabyte range and probably a very large percentage of that is unnecessary.

Congrats on releasing. This is what I consider love of the game.

I sent the link to my buddy who owns a shop that specializes in analog synths. Our shared love of drum machines and electronic music production was how we became friends. Dunno if he's nerdy to the point that he'll install it, but I'm certain he'll also love that it exists.

Honestly, this is the best thing that's come along on HN in a while. I was feeling kinda down, but this made my day. And it instantly recognized my m-audio keystation, a feat that Logic could not perform.
Needs audio examples! A YouTube link!
I like your LFO, man!