36, trans, massive nerd, Decker Zine Queen, old anime and games and such. also physical media and shelves. avatar by @bachelorsoft



I made a janky synthesizer, in Decker. It's for the Decker Fantasy Camp 2023 jam!

It features

  • three waveform types
  • one octave keyboard
  • some ADSR sliders that work and took some messy maths and scribblings to figure out
  • configurable pitches for that weird microtonal thing you want to do

Let me know what you think. I know very little about how synthesizers are supposed to work (beyond press note get noise) so I learnt a lot making it.

edit: version 2 is out now! changelog in this chost

edit again: version 3! now with keyboard shortcuts! details here


You must log in to comment.

in reply to @milliesquilly's post:

Nice- sounds great!

If you aren't sick of working on it, one enhancement that's possible is rewriting the waveform generation code to "vectorize" instead of generating samples in an explicit loop. For example, your sinewave function already works equally well on scalars or entire lists in one go:

contrasting sinewave called on a scalar and a list x

The envelope function especially might be a bit fiddly to adapt. A general trick for replacing conditionals is to form piecewise functions by using addition, multiplication, and logical comparisons:

 v:range 5
(0,1,2,3,4)

 v<3
(1,1,1,0,0)

 (2*v<3)+(-5*!v<3)
(2,2,2,-5,-5)

The advantage of a vector-oriented "all at once" approach is it is dramatically faster for Lil. (And sometimes less code, too!) You could generate audio samples on the fly in well under a frame, and open up potential for even wilder tools!

We're talking multiple orders of magnitude faster:

$ time lilt -e "each x in range 100000 x+10 end"
real  0m2.515s
user  0m2.481s
sys   0m0.022s
$ time lilt -e "10+range 100000"
real  0m0.039s
user  0m0.022s
sys   0m0.010s

Not only is it faster in wall-clock time, it's especially faster in the way Decker measures script execution: "ops". Some Lil statements are made up of many ops, but primitives like "+" and "range" are a single op. Every frame you are given a certain quota of ops to perform (40,960), and if that's exceeded scripts are paused until the next frame, to ensure liveness.
If you execute a loop 1000 times, every op in that loop is executed 1000 times, plus the overhead of the loop itself. If you execute a similar expression in one go on a length-1000 list, it could take less than 1/1000th as many ops to execute. This pleases Lil.

Pinned Tags