Kyma Studies

I wanted to document a tutorial on how to create a granular engine from scratch in Kyma. This is to help codify some of my own learnings but also based on requests from the community. Additionally, as I’ve lurked on Lines for years I also wanted to start contributing. I’ve tried to write some of this as general purpose, though there may be more than a few references specifically to how to accomplish specifics in Kyma.

Some background:

While Kyma has some of the most powerful and creative granular prototypes available, I wanted to explore further customization and apply some core principles, as I see them, in new ways.

Some of the more compelling aspects of granular synthesis rest in the idea of per grain definition of parameters. Generally, the per grain parameters have been confined to global values for amplitude envelope window, window shape, panning, pitch, duration, and sample start position. These are often coupled with a jitter function and allow for each grain to vary within these boundaries.

I suspect many of us have wanted to explore: what happens if we could add additional processing to each grain and see where that takes us? For example, what happens if we add waveshaping or filters at the grain level? What about per grain delay networks? Applying the envelope to parameters other than amplitude seems like another rich opportunity, even if strays afield from the core theory and techniques.

One of the principles that I am going to try and adhere to is the idea that there are global controls for any new processing parameter we add and we use randomization to define the per grain control. This may not be a requirement for you, but it is my personal preference based on a love of exploring how random interference patterns create rhythm and texture.

I also like the idea of a strong central reference point and using randomization to create the cloud of variations. One of the questions that arises from that position is the nature modulation and whether it should be continuous or defined only at the start of the grain cycle which a few notable, such as the amplitude window. I am going to split the difference here and try and stick with defining the per grain parameters at the beginning of the cycle and yet allow for the envelope window to be mapped to other parameters and even the ability to create multiple envelope windows as long as they share the same duration as the primary window.

I am not really one for theory nor dogma so it is very likely we may stray or at times not implement those constraints within the examples explicitly for the sake of clarity and time.

Building the foundation granular engine

There are many different types of granular synthesis approaches. This example is going to use sample playback at the core since that one of the most commonly referenced. However, feel free to replace the simple sample playback parts with a morphing spectral engine, complex oscillators, or modal synthesis.

I apologize if this gets too Kyma specific. If there are any questions about how to generalize the approach or other potential solutions, please ask.

I made one upfront design decision which is that the timing and trigger logic is done at control rate (in Kyma that once per millisecond). If you are using grains for high-resolution tasks like time-stretching or pitch-shifting, I recommend replacing those sections with audio rate equivalents though that will add complexity and add DSP overhead. The specific thing you’ll have to build that isn’t currently in the default Kyma audio library is an audio rate set-reset latch (not hard but a bit involved for the purposes of this tutorial). Otherwise, everything outlined here can be done at sample accurate, audio rate. For most applications, the timing and trigger logic doesn’t require that level of accuracy.

Lastly, there are many ways to likely solve several of these problems, my solutions build ideas and concepts put forward by those who came before me and any mistakes, shortcomings, or limitations are my own. I welcome and encourage questions as well as suggestions on how to improve the examples found within.

Step 1: Create a master duration time index

The first thing we need a time index (aka ramp) that does the following:

  • Listens for a trigger
  • Once triggered, it should not re-trigger until its cycle is complete.
  • Can vary in duration, including via randomization

Starting in the under left, we have a simple random trigger generator. We’ll talk more about differing approaches to triggering your granular cloud but for now, I’ve put in a simple static trigger that repeats every 200 ms to make it easy to test and debug.

The trigger enters a simple set-reset latch. The reset for this latch is the end-of-cycle for our index. We test for the end of the cycle by feeding the output of the index into a threshold comparator that triggers once the index approaches 1.

As a precaution against unexpected values, I force positive values to 1 and discard the rest. This is not strictly needed, but is a habit I got myself when dealing with what could be arbitrary trigger sources and I need to create triggers that may be passed around to different destinations which may react to negative values in expected ways. This is a functional approach of validating on output which can be helpful for debugging complex flows.

Moving to the index: our trigger starts the cycle and define the grain duration length.

Jitter is broken out into a separate module. For now, the jitter is a simple random number generator between -1 and 1 that is triggered at the start of each cycle. Jitter is a whole deep dive topic in itself. My preferred approach is to create an expression for minimum and maximum values and then have the jitter distributed between those end points. However, for this tutorial we are going to use the more common “center point” method for now.

Example of a jitter expression

As noted previously, we take the output of this index and feed it back to the start to fetch the end-of-cycle. There are other ways to accomplish this in Kyma including using a sound to global controller but the feedback mechanism is reliable and sample accurate if you happen to want to replace other parts of the triggering logic control flow with sample accurate methods.

Now we have a duration ramp (index) that we’ll use to determine the grain cycle and how long that single shot cycle will run.

Step 2: Create sample playback index and a single grain

We are not done with indexes. For a granular sample playback we need a minimum of two indexes. The second index is used to control the playback of the sample within the grain.

Here is the next evolution of our flow that will define the majority of what constitutes a grain cloud.

The initial structure to the right remains the same. In the middle we add a new index. What is different about this index is that its duration controls the pitch of our sample buffer. So if the duration of the sample is 2 s, to hear the sample at normal pitch, the index will be 2 s. If we want to pitch up by an octave, we shorten the index to 1 s. Kyma has a pre-built control for rate which multiples the duration by the rate value. For now, we’ll use a fixed value and a simple jitter control though there are varying approaches if you want to make this easier to use with a keyboard or traditional note sequencer. One thing to note here is that the sample selection is scripted so that the ramp automatically calculates its length based on the file selected.

We are going to want separate controls for sample start with start jitter but we need to decide how to handle the end of the buffer. The most common approach is to loop back to the beginning of the buffer. To do that, feed the index and the sample start offset into an Add Wrap. This takes the sum of the inputs and wraps any value less than -1 and greater than 1 around. For example, a value of 1.5 becomes -0.5.

In the off chance you want the index to start playing in reverse if the index overflows the -1 to 1 range rather than loop back around, look at the FlipWrap example in the Kyma community library.

We now have an index to control our sample playback buffer so let’s take the final steps to create the single mono grain. First is to apply a waveshaper being indexed by our duration ramp and use that to control the shape of the amplitude of our grain.

As a side note, the envelope shapes for the vast majority of granular approaches are window functions. This is why we see Gaussian, Hann, Hamming, etc, as common selections. There are many justifiable and correct reasons for this, especially anything that needs an even amplitude distribution between overlapping grains (for example, pitch and time stretching). However, if we are looking past those use cases into more experimental areas, there is no reason we can’t use a variety of shapes or even vary the shape per grain. What is important is that the envelope starts and ends at zero with a bit of a fade. If this doesn’t occur, you’ll experience DC offset clicks.

Lastly, add our pan control and associated jitter.

So we now have a single grain that encompasses most of the basic controls found in grain clouds. If you find that you want to be modulating the sample start or sample jitter in real-time, it is recommended that you add a sample and hold that is synched to the grain start. Otherwise, you’ll get what are, likely, undesired discontinuities.

Spend some time testing and debug your single grain before moving on to create a cloud or experimenting with additional layers of complexity.

Step 3: Create a cloud of grains

In Kyma creating a cloud of grains is super easy. Slap a replicator on the end, do not replicate any of the controls, and make each random number generator have a unique seed value per voice. The number of replications is the maximum number of grains.

For the trigger, replace your steady state click with a noise generator into a threshold. Make sure the noise generator has a per voice random seed. The threshold will become the density control. For random controls mixed with periodic, add trigger generators and mix it with the random output.

So now you have your own modular grain cloud generator. You can likely figure out where to go from here in terms of adding processing on a per grain basis. You can also immediately see opportunities to use different random distribution curves on your jitter (something not possible with most other options). The next post will explore some of these branches.


What a great resource. Hi from the Kyma slack and MW (Geoff / Gosh) also.

1 Like

I ran a bit late on this than otherwise planned. The bulk of the text was finished a week ago but then I got sick and wasn’t able to make progress on the audio recording examples. I was able to do a couple quick examples today so we’ll just go from here.


This section will cover some examples adding per grain processing beyond those generally found with most granular synthesis examples. If you have done part one of the tutorial, you have your own basic sample cloud generator.

Future posts will cover fine tuning parameters into useful and, sometimes, inter-dependent ranges, designing different random distribution strategies, creating a modulation network, replacing the sample player with other sound generators, and scripting optimizations. But first we need to get through some basics of adding effects or signal processing to individual grains.

One note of caution as we head down this path, we are likely going to hit dsp limits pretty quick. Having 16+ copies of a full signal flow including filters, delays, with random generators everywhere adds up. There are some places where you don’t need unique random generators so you can use a shared noise source. It is likely best to stick to the built in filters rather than use custom filters by NeverEngineLabs or the Kyma community. Given the use cases here, the built in filters are file.

Step 1. Add a per grain filter

Perhaps the most obvious and immediate thing to try is a per grain filter. This is as straightforward as it sounds. Add a filter between the sample player and output pan.

For controls, a frequency offset plus jitter is a good place to start but I also like adding a ‘window modulation’ here. Basically, this is taking a window and using it to modulate the frequency. You can use the same window as the VCA for a low pass gate effect or use an independent window.

For the jitter, you can use the same technique above to jitter (aka a capytalk script) or you can use a noise source that is fed into a sample-and-hold triggered by your common grain start trigger.

One recommendation here is to apply a transform function so that your modulation is exponential rather than linear. There are a number of ways to do this but my preferred is something with some dynamic control so I tend to use the somewhat under-named ‘Saturator’ Kyma sound to create a controllable curve transform function but you could also use a capytalk formula. You can also make some decisions here on whether you want unipolar or bipolar modulation.

Step 2. Panning strategies

Next we are going to expand on the panning strategies just a bit. The first recommendation is not to use a full spectrum random source (aka white noise) but rather something an exponential or normal distribution. This keeps your pan jitter from being so extreme or ping-pong effect, unless that is what you seek!

Next add a window to control the pan movement. This isn’t really all that interesting or useful if you lean towards the very short grain within your sounds but if you are experimenting with long grains, especially those greater than 1 second, you may find a window here can add some interesting movement to your cloud. For now, use a separate window from your amplitude or filter. We’ll discuss creating a modulation matrix in the future that could possibly reduce the number of separate windows or you can go wild with as many windows as you like.

Step 3. Per grain delay controls

The last example for this section is going to be a delay. This is likely my favorite simple addition to the granular engine, so far, and pairs well with the above filter and panning additions.

Add a delay to the process flow. You can do it pre-pan (mono) or post pan (stereo). I’ll leave it to you to decide which is better.

For the delay, you have several approaches to the delay length but they more or less boil down to synched verses un-synched.

For unsynced, add your delay, set your max delay time and add your delay scale offset and jitter controls. Consider doing the same for the feedback controls.

For unsynced, we are going to assume you are using the center point method described above. Other mechanisms work but the math may be different.

Step one is determine the max duration you expect to have. In the time scale, use the duration formula in your duration time index divided by the max duration. If you are comfortable with scripting, you may have seen the opportunity here to leverage scripts rather than copying around duplicates of the same formulas.

Before we go too far, let’s add a damping control to the feedback. Here is a basic delay damper:

As you test this sound, experiment with different smoothing and interpolating values for the delays. If the feedback is such that grain re-triggers before it decays away, those controls will have a profound impact on the sound. If you want to explore sound-on-sound types of effect, you may want to consider a ‘tape delay’ approach to your delay sound rather than a digital interpolating delay that is standard in Kyma.

Here is my signal flow to this point. I’ve merged down the jitters functions. They use the technique described above and we’ll be diving deeper into those next.

Sound Examples
Here is a collection of sound examples. Two are the same patch and default parameters with only the sample changed. The sample is played first and then followed by the granular version. None are ‘played’ or have time based modulation but are simply compiled and recorded to disk letting random jitter and delay builds and fades over time create movement and variations. One example demonstrates a “tape delay” verse “digital delay” approach to changing delay lines. The tape delay isn’t modeled to sound like tape with EQ curves and saturation but reacts to changes in delay length like tape does rather than how typical digital delays may react with interpolation. I owe many thanks to Pete Johnston (The Tape Gallery, Digico DSP designer, etc) for his work on creating that core delay patch for the Kyma community.


Consider and test some related areas:

  • Per grain envelope jitter (add jitter to your envelope shape morphing control)
  • Define filter frequency as a ratio of the duration. For example, a high-pass filter that removes DC signals that remnants of low frequency waves that are greater in duration than the grain length
  • FM filter as a ratio of the duration. An easy way is to use windows that are already 2x, 4x, 8x iterations or you can phase lock at ratios of grain duration.
  • Other per grain processing approaches. For example, saturation, distortion and wavefolding provide a lot of timbre options.

Up Next

Next section will focus on distribution strategies for jitter controls.


This is an incredible resource. Thank you for sharing. Any plans to share the source files?

I may package some various examples. I keep finding new areas to explore or approaches so I never go back and tidy up aspects of it into an end state so I tend to leave in various hacks or inelegant approaches that work but may require fixing if someone wants to build on them.


This is really good and well layed out material!
Looking forward to reading and working with the next chapter.

This is a fantastic resource, thank you. Hopefully I can contribute as well at some point.

just wanted to say thank you for this awesome tutorial!!
Will definitely come back to that when I have a bit more time and check it out in detail!

Do you mind giving a hint on how to make the sample rate setreset / latch mechanism for sampleaccurate grain triggering?
Loving the tutorials, but a bit stuck on how to do this.

latch.kym (872 Bytes)

Here is an example to get you started. This is based on work contributed via the kata community. I try to limit the number of feedback loops in my sounds so I tend to stick with the capytalk version most of the time.


Random strategies

This section may be a bit meandering. There are so many avenues to go down and side topics to explore when we talk about randomization.

Within music contexts we talk about topics like white, pink, blue, and grey noise. Within the context of jitter as it applies to granular synthesis, it isn’t always clear what the randomization strategies are being deployed. Is it pure random (as much as such a thing is possible in digital systems) or is weighted in some way? For example, a glance at the Tasty Chips GR-1 manual discusses ‘spray’ aka jitter but doesn’t define the behavior nor seem to provide control over it. The wonderful Borderlands app is no less opaque. Madrona Labs Kaivo, one of my personal favorites, does give us control over a noise source and hence some jitter structures but not all.

Before we go too much further let’s define two related concepts when it comes to randomization within our context. Jitter is typically the random variation generated at a sampling point. However, we have another important concept, which is time based randomization. This can be expressed as the chance that a trigger should occur at sampling point. These names may be different in different implementations (for example, the aforementioned GR-1).

For our proposes and to follow Kyma convention: density is the chance a grain will be triggered and jitter is the amount of variation for a given parameter of the grain.


While Kyma has a wealth of CapyTalk expressions for generating random values we are going to stick with a generalized approach that translates to almost any environment and may be useful in understanding how other systems are approaching their implementation.
We’ll use a white noise as our primary source. White noise will generate an even distribution of values over a reasonably high number of sample points. If we only take 10 samples of white noise, the values may be weighted towards some values but as we add more and more samples, that weighting will be more even. After 10,000 samples, any weighting will be extremely small.

An easy way to create different weights is use a waveshaper, mix multiple noise sources together, raise the value to a power, or use the ‘saturator’ module. The ‘saturator’ module is a bit deceiving in name, to be honest. At the core is a S-shaped (polynomial) waveshaper that can be dynamically adjusted. Feed a 1-order saturator with a curve of 1 with a triangle and you’ll receive a sine output.

Let’s take a bit of time to explore how white noise can be redistributed using the later approach.

As you can see, via this approach we can shift our values to the extremes so there a re a lot of little values and fewer larger values or the inverse. When these values are sorted in order, they can make up a power law or exponential curve depending on our approach. Power laws are extremely common in the natural world and as such we recognize and react to them in very different ways to random or sudden state changes.

Our brains love pattern matching. There is a vast amount of research that can occupy years of reading and endless exploration on the topic of how pattern matching influences our perspective of the world around us, how we may make decisions, and ultimately evolutionary responses. One of the fun aspects as it relates to granular synthesis and music in general is that we can exploit the listeners’ need to pattern match. When we decide to generate values out of order, our brain may or may not reconstruct the distribution curve depending on factors including length of time between sample points, number of data points, deviation range, and what is being modulated. Variations within that expected model can engage our brain and create moments that range from engagement and understanding to confusion and emotional responses as varied as calm, delight, and anxiety.

Building on this concept we can modulate or vary our distribution patterns over time to interesting effect. We can have the jitter slowly drift the center point or narrow the jitter down over time. Or we can have multiple overlapping distribution patterns and see what macro patterns emerge. Another suggestion is to feed the jitter into “interpolateFrom: To:” CapyTalk expression and modulate the From and To values whether by hot parameters or via modulation. Using this method we can easily create a min / max for your Jitter boundaries.

Time based variations and density

Earlier, we defined chance as the likelihood a trigger would happen at our sample point.

Our sample point could be:

  • 44,100 times per second (global sample rate example)
  • Triggered by an event
  • Time based (every 100ms)
  • Combinations of the above

Density is typically a combination of the number of maximum grains possible and the likelihood they will be triggered.

If we have 8 grains, we may want them to be spread out so they don’t all trigger at once. We can take a couple of approaches here. The basic approach is just set the chance low enough that there are unlikely to be multiple triggers at once and, over sufficient time, the spacing will even out over time. At the other extreme is using grains in the context of pitch shifting. In that case, we offset the grains so they are out of phase so that as one fades out, the other grain fades in, providing a smooth amplitude response.

There are other times where we may want to cluster our triggers for rhythmic or other effects. There is a handy set of CapyTalk expressions called randomTrigger, randomExpTrigger, normalTrigger, and BrownianTrigger. I highly recommend using and exploring these expressions as they are useful in a number of situations and tend to be my default choices as they are quick, easy to use, and efficient.

But in the spirit of building some examples from scratch let’s try some other approaches.

A simple structure is a noise source into a threshold detector to create the trigger. Depending on the density we want, we have a few approaches to slow down the rate of triggers. We can either scale everything down so the chance is very small that the grain will trigger upon each clock cycle or we can slow down the clock. The later approach is compelling if we want to work with longer grains or want them to trigger on a clock division, for example, the probability that a grain will fire on each 16th note.

To create clusters of triggers, we dynamically modify the threshold. For example, add an LFO to the threshold going from 1 (no triggers) down to 0. To see how this can cluster triggers, let’s slow the sample rate down to one sample per 50 ms. We can see the triggers clusters around center points (the top of the LFO) every 6 seconds (the frequency of the Triangle LFO). By adding a distribution here we can create different distribution patterns by applying our methods outlined above in the jitter section.

There are other approaches to explore here. One easy example is to gate the noise by a pattern generator (step sequencer, logic gates, euclidean, etc). Add a gate duration so you can control how long the gate is open. Then pass that through to the threshold. You can use this to create a swing or simply a gated burst of random triggers. Other examples include random ratchets clusters or oddities like controlling trigger density based on an envelop follower.

Correlated random changes

If you want to correlate your changes so that they are a deviation from the prior value rather than a completely random value, you can use a feedback loop and interpolate it with the input. Here is a rough example of the Buchla 265 stored random voltage with correlation:

This is very much a brownian or random walk and there are examples within the prototype library using a lossy integrator as well as a pre-existing Capytalk expression for this. I recommend those for most implementations within Kyma. The feedback method may be useful to create correlated signals or other types of governor systems when those other methods may not be appropriate.


In the interest of time and getting this part published, I am going to stop here. We didn’t even get into topics like hotpink noise, chaotic maps, random index distributions, or creating interdependent systems where one set of random values influence other parts of the structure. If there is one takeaway I want to impress from this section it is to encourage you to explore beyond the normal random distributions choices offered.

I may have glossed over a lot here so feel free to ask questions. I’d love to hear your thoughts on your different approaches to building randomization into your structures.


Following up on the last post on random strategies here is an example of patch outlined in Allen Strange’s book when discussing the Buchla 266. Within this patch is a basic implementation of QRV that runs at audio / sample accurate rates. Also included is the new oversampling filter built by the kyma kata group.

Kyma patch (912.3 KB)

I suggest porting this into the heart of your custom granular engine. :slight_smile:


This is incredibly helpful, I really hope you will do many more.

Thank you for the feedback. I have more to add here but I got a bit distracted with some other explorations. I would welcome some suggestion on particular areas or topics that people may find helpful so please let me know if there is an aspect that could warrant more of deep dive.

This isn’t specific to grain clouds and perhaps it may be time to update the title of the post :slight_smile:

Modulation Matrix

This is a bit of dive into building a modulation matrix. I wanted something that provided some quick switching between modulation states or playability that we find with a modulation matrix made famous by, though not exclusive to, EMS. There isn’t anything particularly difficult about this in Kyma so much of this post may be fairly 101 so to compensate, I provided a starting kym for the capytalk version a custom granular engine discussed above. :slight_smile:


Before we get to the matrix implementation let’s quickly review some different approaches to mixing of sources. Here are those that we’ll cover:

  • Sum Mix
  • Wrap Mix
  • Fold Mix
  • Self-normalizing Mix
  • Product Mix

These are likely fairly well understood by most but I want to highlight them again as a way to encourage people to look beyond summing mixers as I know we all tend to default to those for that vast majority of our use cases but when it comes to modulate wrapping mixers offer a lot and in some contexts normalizing mixers are very handy.

Sum Mixer (with Clip)

This is likely the most common approach. Input two or more signals are added together and if the sum is greater than -1 to 1, the signal clips. The straightforward nature of this makes it easy to understand what is happening though the likelihood of clipping increases with the number of sources. Thankfully, Kyma mixers include a global input gain that will scale all inputs down so you can easily normalize the mix without having to individually adjust each individual level.

Wrap Mix

The AddWrap and GainWrap prototype takes the sum of the input and if the value exceeds the -1 to 1 range, the signal wraps back around the other side. 1.5 becomes -0.5 for example. This is designed specially for index sounds where you want to mix a number of signals but can also be used for modulating signals. One example would be a sample start where you want to wrap back around to earlier in the file instead of saturate at the end point. Another example would be panning.

Fold Mix

Similar to AddWrap except that when the signal hits the boundary, it reflects back. This is also known as a wavefolder. Example: 1.25 becomes 0.75. The signal continues to reflect if the range exceeds -2 to 2. Due to the method of implementation, there is a practical limit to the number of reflections at least in the implementation I posted on the community library for download. If you need more than 255 folds, you may want to consider another approach.

The Fold/Flip/Reflect approach is also useful for panning. Both Fold or AddWrap can be useful for spreading values so the values don’t clip out at one. For example, say you have 4 voices and you want each voice to have a spread from the other, but you’ll also be modulating that spread or perhaps the center value. Using an add wrap mixer can help keep the spread ratio while using fold wrap will potentially cluster the spread around the ends without them all clipping to -1 or 1.

Normalizing Mix

A normalizing mixer automatically scales down the inputs based on the input values. In the summing mixer, you can manually adjust the input scale down and it will adjust all the scales. If you don’t want to manually adjust those, we can create a quick formula to adjust the input based on the level. This may be useful in a performance setting where you may be making adjustments quickly but want to guard against clipping the signal.

There are two basic formulas, one for signals that are likely to be correlated and one for uncorrelated signals.

Correlated. Using a 4 input example:

1/ ((!Level_1 + !Level_2 + !Level_3 + !Level_4) vmax: 1)

Nice and easy. Basically we add the input levels together and if it is greater than one, we scale the mixer input by the sum total. If all 4 levels were 1, then the scale would be 1 / 4 or 0.25. We added the vmax capytalk so that the divisor never drops below 1.

Uncorrelated. Using a 4 input example:

1/ ((!Level_1 + !Level_2 + !Level_3 + !Level_4) sqrt vmax: 1)

Uncorrelated signals assume that it is unlikely that the signals will stack in a why so the peaks align to saturate the range beyond -1 to 1. Audio signals are uncorrelated. Modulation sources may be uncorrelated but it depends.

Auto-normalizing mix

Rather than adjust based on your fader values, you could auto adjust based on the signal. This requires you determine how you want the signal to adjust. In this case, you measure the input with the slew-rate or bidi follower and then mix multiple followers together in the same formula as above. Here, release value matters but if you are using this for modulation, you may want to adjust it a lot like you adjust compressors where the release fits within the overall tempo and rhythm of the piece. You can definitely run the risk of having the modulate swell in and out or be a bit unpredictable but perhaps that fits with your intent.

Product Mix

Not sure what this is called formally, but the idea here is that the input signals are fed into a product rather than summed.

Example capytalk

(!ModOn_1 true: (ModSource1) false: 1) → Product
(!ModOn_2 true: (ModSource2) false: 1) → Product

This passes a 1 to the product if the ModSource is disabled. As soon as you enable it by having the ModOn value go positive, it passes the mod source to the product.

The challenge here is that you find yourself quickly reducing the RMS down towards 0, especially as the input sources increase, but that could be just the desired effect you seek.

Matrix Mixer

Returning to the matrix mixer. There is a prototype for matrix mixers in Kyma. This takes a set of inputs with the ability to route the inputs to a given output. There are 4 and 8 channel versions. Then using the channel selector, we can route the output to our modulation destination.

Here is an example:

In this example, we wanted an auto-normalizing matrix so we didn’t clip our modulating mix. Rather than repeat the pattern to define the normalizing behavior, we use a script to calculate the to divisor total and generate the control events.

Example files

Here is are two kym files. One is of the matrix mixer above and another is a capyrate version of a custom granular engine. With the capyrate version, I left places for you to insert your own jitter sources. See above for some ideas and examples.

KatainCore-CapyRate.kym (308.5 KB)
ModulationMatrix.kym (205.3 KB)

The matrix mixer creates a highly playable way to switch between different sources and create different mixes quickly but one thing that I discovered is that it isn’t the most efficient use of resources especially when replicated. In talking with the some others, the theory is that by using output routing, we forego some of the optimization benefits within Kyma complier.

In the screenshot above you can see how I am routing the sources to the duration length, sample start, filter, pan, etc.


I highly recommend that you sign-up for Alan Jackson’s Patreon. He has been releasing a new set of modules each month. This month includes some extremely handy files including a number of modules for building a sample rate granular engines, a sample rate clock divider, ASR, DAC, and more. Also included is an implementation of a Blippoo Box by Ian Fenton.

Alan is also providing one-on-one coaching and has been an invaluable resource to creating modules, especially sample rate versions of complex things that may have only been easily accomplished at Capyrate before. He is responsible for talking my brute force approach to the matrix mixer and turning it into an easier to maintain script.


This is always a tricky one to answer since I find which environments work highly dependent on your workflow, interests, and personal preference.

I am not a programmer by trade and you don’t need to script at all to be highly successful using Kyma. Over the years, I’ve picked up a snippets of things that I use where needed or I just work it out the long way and then perhaps someone shows me a short cut that makes reusability easier. I’d say the vast majority of users don’t script at all. Kyma does make use of text commands here and there for control signal and those are handy but it again, it is very easy to use selectively in spots. For many, many years I spent most of the time working with the higher layer flows of wiring together existing modules. Even now that is where I spent most of my time. That said, there is amazing power in Capytalk and I’ve been spending more and more time learning those over the past 5 or so years and that library of commands continues to grow in new and interesting ways. But I don’t consider that true programming and they are easy to use. They also are designed to be used a standalone statements and don’t require structured code.

For me, the limitations mostly are not knowing the best way to solve something and my own limited understanding of certain concepts.

I found Kyma easier to learn than CSound and PD for sure. I liked SuperCollider but I also found Kyma far more powerful and compete, though it has a been a long while since I picked up SuperCollider. It was the first of many tools I’ve tried that just synched with me and I haven’t found anything I prefer better after 20+ years. If I gave up Kyma, I’d spend a lot time inside of Bitwig Grid, return to SuperCollider, or see if Max is finally less frustrating to me.

If you decide you want to pursue that path further, feel free to reach out to me and I’d be happy to do a zoom call to answer questions and screen share Kyma in action more.


I’ve been sick for the past month so not a lot of Kyma or music recently. I started back on an older project this past weekend and ended updating a little helper sound that I think may be useful to others.

Depending on the area of Kyma you are exploring, you may find yourself creating large arrays of controllers. A common example is anything dealing with additive or aggregate synthesis. Other semi-common use cases are sounds like spectral delays or filter banks. You can easily find yourself creating banks of 16-32 or more faders for multiple controls like frequency, bandwidth, or amplitudes. This is great for detailed fine control over the sound but it can be challenging to manage quickly or modulate the array while maintaining relationships between the other parameters in the array.

Perhaps this is my brain gravitating towards patterns but I often find that arrays end up as recognizable shapes or patterns. For example, arrays distributed in a ramp from high to low or triangle shaped. So rather than manually adjusting massive arrays by hand, I wanted to create a simple waveform to array sound.

There are several ways to do this, in the past I used offset triggered sample-and-holds and FFT approaches but I found the following process the most flexible and easiest to modify and troubleshoot, though with some drawbacks depending.

Our signal flow:

Here is the .kym for the above sound:
WavetoArray.kym (66.1 KB)

The core is a replicated sound with an oscillator that is phase offset by the number of values you have in the array. From there you can set the modulation running, freeze the sound, add an offset or reset the wave back to the starting point.

If you have ever created a large bank of faders and wanted to experiment with different settings quickly, I suspect that you can imagine how handy this approach could be. One obvious use case is scanning through a filter or an oscillator bank. Recently, I’ve been using it to control probability values within the nextRandomIndexFromDistribution that then selects notes within a scale. With narrow width but setting the curve to extreme values you can create highlight small areas within the array and then scan through those as well or create notch effect.

I sometimes find that using large banks Sound to Global Controls quickly leads to DSP overloads, especially when modulated. I don’t recommend using STGCs in this way typically. Rather, I recommend hardwiring this directly into your modulation destination where possible.


K_Field Lab

I’ve always heard great things about Fairfield Circuitry and, in particular, the Shallow Water pedal. Happily, the designer includes the signal flow in the spec / manual which makes it a heck of a lot clearer what is going on compared to the general description.

I love using stuff like this as a jumping off point for creating Kyma patches. It sparks ideas but not having the reference example often means I can explore and learn without trying to recreate the canonical version initially.

Let’s start with the modulation circuit. This is basically a filtered noise design that is very similar to the Buchla 265 Fluctuating Random Voltages circuit described above. Alan Jackson recently outlined a correlation script in CapyTalk, reproduced here:

| lastValue currentValue |

lastValue := EventVariable new
initialValue: 0.
currentValue := EventVariable new
initialValue: 0.

(!Rate hz s tick evaluate: (
     (lastValue <+ currentValue),
     (currentValue <+ (!Correlation interpolateFrom:  Noise L to: lastValue))
)), currentValue

Noise L is a white noise generator though you could insert a CapyTalk expression in its place.

By doing in a script, we save adding a feedback loop and the attendant dsp overhead though we give up audio rate resolution. That doesn’t matter in this design since our rate control won’t exceed control rate.

To complete the delay modulation section, Shallow Water has a “damp” control which in the original design appears to independently set the noise filter from the s&h rate and as such results in sharper edges to the modulation. I use a simple filter based on a delay line as the built in Kyma filter doesn’t seem work properly below about 4 hz and as such is ill suited for this use case.

For the post delay filter, the envelope response isn’t specified so I do not know if it is fixed or dynamic based on other controls or input. I prefer having direct control over the env follower anyhow since this allows you to better tune the responsiveness for various source materials. At small values, you can get fuzz-like sounds and is useful for plunky or drums. If you want a dynamic system, I recommend tying the rate to the attack / release so that the rate scales with higher rate resulting in faster attack / release times.

I added a number of minor additions to improve the response of the envelope follower, along with added filters and transfer functions. This is an area the Fairfield team likely spent some time fine tuning to get the responses they sought for their vision and potential source materials they were tilting towards since as a dynamic and interactive network, it can go in many different directions especially as you add additional response points within the network.

This core design is quite a lot of fun and I want a Shallow Water all the more now as I could see it being a great addition to my computer free setup. I’ve also read the pedal has a fantastic boost circuit which is always a big plus.

One delay is nice but we know many of the classic chorus circuits use 3 delay lines modulated by different phases of an LFO. Let’s see if more is better here.

The replicator makes this clean and easy. Instead of different phases of an LFO, we use uncorrelated noise (in kyma case, white noise with a different seed per voice). We add a spread control to create a stereo image and a set of independent controls for each delay line including a rate ratio, level, and invert. At this point we have departed quite a bit from the original design and we go even further and potentially collapse the field by wrapping the whole thing in a feedback loop. That said, three delays, especially with stereo panning is lovely. I played with more than three and it just is a mess after a while so three seems to be sweet spot.

Attached is an example kym files as a starting point. The implementation is a bit rough around the edges and needs a bit of clean up and fine tuning before encapsulation but should get you started.

K_Field_Lab.kym (646.8 KB)


I was working with a multi-tap delay and I wanted to create different tap spacing strategies, one of which would be a sequence of prime numbers.

There is a Smalltalk message called nextPrime that returns the next nearest prime number but if you want to use ?VoiceNumber or ?CascadeNumber to fetch the next prime in the sequence, nextPrime will return doubles of primes at various points in the sequence as the ?VoiceNumber. The alternative is to script it to capture the prime number and use that to fetch the next one.

An example is copied from a version of Ian’s Dattorro reverb implementation:

| primes x|
x := 1.
1 to: 205 collect: [:I |
x:= x nextPrime.

It occurred to me that what I really wanted was different subsets of prime number sequences. We all are familiar with the ‘regular’ sequence of prime numbers but mathematicians do love to a clever game or exploring various formulas to create different prime number subsets.

To implement these completely in code is beyond my needs, desire, or skill for a ‘quick’ afternoon project and since I rarely need more than the first 32 of any subset, it was easier to copy them out by hand into lookup tables which are used to populate a Smalltalk dictionary object.

|  primePresets primeArray  |

primePresets := Dictionary new.

add: #regular ->  #(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131);
add: #chen -> #(2 3 5 7 11 13 17 19 23 29 31 37 41 47 53 59 67 71 83 89 101 107 109 113 127 131 137 139 149 157 167 179);
add: #higgs ->  #(2 3 5 7 11 13 19 23 29 31 37 43 47 53 59 61 67 71 79 101 107 127 131 139 149 151 157 173 181 191 197 199);
add: #eisenstein -> #(2 5 11 17 23 29 41 47 53 59 71 83 89 101 107 113 131 137 149 167 173 179 191 197 227 233 239 251 257 263 269 281) ;
add: #gaussian ->  #(3 7 11 19 23 31 43 47 59 67 71 79 83 103 107 127 131 139 151 163 167 179 191 199 211 223 227 239 251 263 271 283);
add: #pythagorean ->  #(5 13 17 29 37 41 53 61 73 89 97 101 109 113 137 149 157 173 181 193 197 229 233 241 257 269 277 281 293 313 317 337);
add: #isolated ->  #(2 23 37 47 53 67 79 83 89 97 113 127 131 157 163 167 173 211 223 233 251 257 263 277 293 307 317 331 337 353 359 367);
add: #ramanujan ->  #(2 11 17 29 41 47 59 67 71 97 101 107 127 149 151 167 179 181 227 229 233 239 241 263 269 281 307 311 347 349 367 373);
add: #harmonic ->  #(5 13 17 23 41 67 73 79 107 113 139 149 157 179 191 193 223 239 241 251 263 277 281 293 307 311 317 331 337 349 431 443);
add: #long ->  #(7 17 19 23 29 47 59 61 97 109 113 131 149 167 179 181 193 223 229 233 257 263 269 313 337 367 379 383 389 419 433 461);
add: #euler ->  #(19 31 43 47 61 67 71 79 101 137 139 149 193 223 241 251 263 277 307 311 349 353 359 373 379 419 433 461 463 491 509 541);
add: #irregular ->  #(37 59 67 101 103 131 149 157 233 257 263 271 283 293 307 311 347 353 379 389 401 409 421 433 461 463 467 491 523 541 547 557);
add: #good ->  #(5 11 17 29 37 41 53 59 67 71 97 101 127 149 179 191 223 227 251 257 269 307 311 331 347 419 431 541 557 563 569 587);
add: #lucky ->  #(3 7 13 31 37 43 67 73 79 127 151 163 193 211 223 241 283 307 331 349 367 409 421 433 463 487 541 577 601 613 619 631);
add: #super ->  #(3 5 11 17 31 41 59 67 83 109 127 157 179 191 211 241 277 283 331 353 367 401 431 461 509 547 563 587 599 617 709 739);
add: #happy ->  #(7 13 19 23 31 79 97 103 109 139 167 193 239 263 293 313 331 367 379 383 397 409 487 563 617 653 673 683 709 739 761 863);
add: #emirps ->  #(13 17 31 37 71 73 79 97 107 113 149 157 167 179 199 311 337 347 359 389 701 709 733 739 743 751 761 769 907 937 941 953);
add: #safe ->  #(5 7 11 23 47 59 83 107 167 179 227 263 347 359 383 467 479 503 563 587 719 839 863 887 983 1019 1187 1283 1307 1319 1367 1439);
add: #balanced ->	#(5 53 157 173 211 257 263 373 563 593 607 653 733 947 977 1103 1123 1187 1223 1367 1511 1747 1753 1907 2287 2417 2677 2903 2963 3307 3313 3637);
add: #circular ->  #(2 3 5 7 11 13 17 31 37 71 73 79 97 113 131 197 199 311 337 373 719 733 919 971 991 1193 1931 3119 3779 7793 7937 9311).

I restricted my list to sequences where the 32nd digit was less than 10,000 with most being less than 1000. This gives us 20 options.

Now that they are in the dictionary, we can collect and unroll the array back out of the dictionary named primePresets.

"Build a collection of arrays based on Dictionary of Primes"
primeArray := (1 to: 32) collect: [:i | 
	with: (0@ ((primePresets at: #regular) at: i)) 
	with: (1@ ((primePresets at: #chen) at: i))
	with: (2@ ((primePresets at: #higgs) at: i))
	with: (3@ ((primePresets at: #eisenstein) at: i))
	with: (4@ ((primePresets at: #gaussian) at: i))
	with: (5@ ((primePresets at: #pythagorean) at: i))
	with: (6@ ((primePresets at: #isolated) at: i))
	with: (7@ ((primePresets at: #ramanujan) at: i))
	with: (8@ ((primePresets at: #harmonic) at: i))
	with: (9@ ((primePresets at: #long) at: i))
	with: (10@((primePresets at: #euler) at: i))
	with: (11@((primePresets at: #irregular) at: i))
	with: (12@((primePresets at: #good) at: i))
	with: (13@((primePresets at: #lucky) at: i))
	with: (14@((primePresets at: #super) at: i))
	with: (15@((primePresets at: #happy) at: i))
	with: (16@((primePresets at: #emirps) at: i))
	with: (17@((primePresets at: #safe) at: i))
	with: (18@((primePresets at: #balanced) at: i))
	with: (19@((primePresets at: #circular) at: i))

Now within the replicated sound, I can use the ?VoiceNumber or ?CascadeNumber to fetch the value at the position within the array and use it to set the tap time.

(!PrimeType into: (?primeArray at: ?VoiceNumber))

Make your !PrimeType control in the VCS size 0 to 19 to cycle through the dictionary list. Add the list as VCS tick markers to show the names.

It is worth noting that a you could also do this entirely within the CapyTalk expression select: of : as what is created above is a 2d nested array. The advantage of the dictionary approach is for long lists, it can be easier to read and remember which row within the array belongs to which named entry and that can be useful during troubleshooting. The expression select: select: of: creates a 3d array.

Next post will insert the above example into a multi-tap delay.


Hi, not sure if anyone reading this thread is aware of it, but the NeverEngineLabs Wireframes library for Kyma has been on GitHub for a while now.