Is there any way to make this Tdef more efficient?

Hello all,

I have been studying Nathan’s videos (SynthDef - YouTube) and am liking the flexibility he demonstrates in Routines, but am running into a limitation. This limitation is related to too many nodes being generated/ lack of efficiency when operating at higher bpms / tempo.

In the code below, I am using a Tdef to run a Routine that plays a Synth. The Synth is referencing a Synthdef that chops up samples from a Buffer that is saved locally.

The problem I am having is that when I really crank up the bpm, far too many instances of the synth are being created and it’s causing a lot of overflow in the node tree. Please see screenshot below. Is there anything I can do to give myself more processing power, or make this more efficient? I’m not trying to really go too far- only up to 300-400pm for quick chops. Using patterns, I would do this with /dur argument and crank it to 1/32 or 1/64. Thank you!


SynthDef(\slice, {
       arg rel = 0.001, rate = 1;
       var snd, buffer;
       buffer = \;
       snd =, buffer, * rate, startPos: \ / 16 *;
       snd = snd * Env.asr(0.001, 1, rel).ar(Done.freeSelf, \;
       snd = snd ! 2;\, snd);


 	Routine {
 		var s;
 		var bpm, beat, tatum;
 		var bufGroup, bufBus;
 		s = Server.default;
 		bpm = 170;
 		beat = 60 / bpm;
 		tatum = beat / 4;
 		loop {
 			var buffer, rel, rate; { |slice|
 				var synth;
 				if (slice % 4 == 0) {
 					buffer = [1, 2, 3].choose;
 					rel = [0.001];
 					rate = [1];
 				s.makeBundle(s.latency, {
 					synth = Synth(\slice, [buffer: buffer, rel: rel, rate: rate, slice: slice]);
 				s.makeBundle (s.latency, {
 					synth.set(\gate, 0);
).play(quant: 4);

Node tree after running for 20ish seconds (node looks different because I am grouping here, but functionality the same as above):

Thanks! Are you on Windows by any chance?

Here is a reproducer:

var s;
s = Server.default;
	SynthDef(\bla, { Env.asr(0, 1, 0.01).ar(Done.freeSelf, \ }).add;
	s.sync; {
		var synth;
		s.bind { synth = Synth(\bla); };
		s.bind { synth.set(\gate, 0) };
	"wait for it...".postln;

As the delay between nodes is 0.001s and the decay time on the env is 0.01, in theory there should be about 10 nodes active at a time. But even after waiting 5 seconds, it seems that there are a bunch of zombie Synths on the server.

I thought this was related to udp on windows but the problem does NOT go away if I set the protocol to TCP:

Server.default.options.protocol = \tcp;

This is certainly a gotcha – a pretty old one, at that – but there’s not a good way to remove the gotcha.

We don’t do it very often, but it is allowed (and should be allowed) to start a Synth with the envelope gate closed.

What is happening in this case is: because of the very short time between attack and release messages, the two messages are being processed in the same control block.

  1. /s_new, setting gate = 1.
  2. /n_set, setting gate = 0.

… in that order.

Because they are processed in that order, gate takes the latest set value = 0. (gate is kr, so it can have only one value for the control block – there is no way to make it 1 at the beginning of the block, and 0 in the middle.)

So the synth begins with gate = 0. Now, there is no way to tell the difference between “the envelope started and closed immediately” and “the envelope never started.”

If the envelope never started, the correct behavior is to keep the synth.

A workaround:

	snd = snd * Env.asr(0.001, 1, rel).ar(Done.freeSelf, \ +; guarantees that the gate will be open for at least one control cycle.

Btw @finalspyro it’s helpful if you format your code blocks as code blocks, not as quotations.


Hey Nathan,

I am using a Mac M1 with Ventura 13.1.


Whoa- interesting! Thank you for the explanation as well as the workaround- I just gave this a try and it is working as expected.

I’ve also updated my post to include code blocks rather than paragraphs. Apologies.

Thanks again!

1 Like