Opinionated Advice for SuperCollider Beginners

i think thats exactly the discussion which has to be made.
what is programming? what is contemporary music?
i went way to far on the programming part and got stuck with making music. but where do these meet?
i often get reminded when reading these threads that i have no idea of programming.

i have been digging through the history of late 90s early 2000s laptop music recently https://f4lsch.bandcamp.com/ and https://editionsmego.bandcamp.com/ (great mix btw: Secret Thirteen Mix 179 - Mesmeon - Secret Thirteen), most of the people who made forward thinking, genre defining records didnt invent the software by themselves: Get Out | Pita or Hotel Paral.lel (2022 Remaster) | Fennesz you have a large amount of mego artists who used for example “apPatch” on SC 2 https://etheses.whiterose.ac.uk/26172/1/550249.pdf or Xynthi. Its also true with more recent releases on SUPERPANG or ETAT dwelling on the nuPG with some flaws in its implementation in relation to SC Ugens (S&H for each Pulsaret so no FM per grain with GrainBuf or no overlapping pulsarets with PlayBuf).

1 Like

The advantage to system building is that you can build it up up over a long period of time. I have had a lot of success with this approach…and a lot of failure. The nice thing is I keep the successes and throw out the failures. I have some sound processing modules THAT I MADE OVER 20 YEARS AGO and still use in almost every performance today. I will probably make something next week that goes in the garbage next month.

This being said, I am not really making pieces. I am mostly an improviser who is building a language over time. My dissertation is called “Laptop Improvisation in a Multidimensional Space.” It isn’t about composition, as I decided a while back that the instrument, at least in my hands, is better at improvisation than at composition. My compositions tend to not be in the form of SC programs, although some of them are. I have found SC to be very bad for distribution of “pieces” even while it is great at composing.

The balance between creating good music and making good code is constant. SC, because of its idiosyncrasies, shall we say, tends to attract people who like to code and might find code attractive. Therefore we need to learn to see the forest through the trees. It is a constant struggle that we all need to be aware of.

Sam

2 Likes

I have some great success stories - I’ve built instruments over the last few years that felt at the time like way-too-big engineering nightmares, but after diligent attention and care (I’m talking a period of a month or two, not years…) they matured into very flexible, exciting things. I’ve built abstractions that let me work out ideas with a ton of fluidity and speed, and let me make great sounds all afternoon without any “engineering-y” work to be done at all.

But there are some caveats here:

  1. I also have FOLDERS full of half-baked ideas, buggy implementations, and “musical systems” (though this particular kind of thing I, like nathan, gave up on years ago). The amount of wasted time is immense, though these days I tend to be quite selective about what I spend my time on engineering-wise.
  2. I have 15+ years of professional software development experience, including large-scale work on systems… a fair amount more complex than any of my SuperCollider patches. This is a VERY particular and i think relatively unusual perspective to have in the SC community.

If I’m thinking of “success stories”, a few commonalities:

1. Small, simple, well-designed things that solve a specific problem I run into all the time.

The payoff with these can be fantastic, because the cognitive load of having to deal with some gotcha or clumsy way of doing something in sclang takes attention and energy out of every studio session. These are the equivalent of those hooks and fasteners you hang cables on in your studio - you shouldn’t spend ALL your time just re-cabling your setup, but if you trip over cords every day it’s worth spending a day or two solving the problem.

But, caveat: My ability to remember and use these nice little abstractions is limited: I probably have 15-20 of these in my core rotation, built up over many years. More than this and I simply won’t remember how they work, how to use them, or how to fix them if I find a problem. I have the energy and attention to care for a small menagerie of these animals, but definitely not a large one. And finally: your practice changes, so a useful thing now may not be useful in a few years.

2. Complex, highly flexible instruments / tools / libraries

I have have maybe 5-6 things like this. These took a LOT of time to build, and still take some effort to maintain. But, they are at a level of abstraction of a good VST plugin - they have reasonable parameters, flexible behavior, they sound good / work well, and I can throw them in something with no parameters or setup and they start working immediately.

All of these that I would consider successes started with code-jam style development - I’d see an opportunity that felt useful enough to really solve and not just hack around on. I’d spend a weekend or a week building a quick-and-dirty prototype, and make music with it. If it works well, sounds good - like really good, game-changer good - then I’ve made time to properly tackle the problem, make the code solid. This has meant REAL development: not just hacking around to make it a bit better, but really thinking about myself as the user, thinking about how I want to use it and what would make it better for me.

Usually when I reach a critical mass of “I really want it to do this!”, I’ll put in a well-defined chunk of work into it - e.g. a weekend or two - and then leave it alone for a while. The best things tend to grow like an onion, adding thing layers onto what was there before. I also have lots of things that never made it out of the first code jam phase, but these were often learning experiences - and one weekend for a failed experiment plus some learning is relatively low cost for me.

3 Likes

Some great responses!

How do you manage/interact/import them?

How big do your files get? I’ve always found that after 1000 or so things start to become unmanageable, particularly if the code needs to be executed it a particular order.

About 500 LOC for a standard 5-minute composition. I almost always have just two parenthesized blocks, one for SynthDefs and one for Routines, which take up about 50/50. Having many executable regions is a bad idea in any context other than live coding.

My code is not very software-y, it’s mostly data. The bulk of it is UGen graphs and manually sequenced rhythms and pitches.

Ah, unfortunately that simply isn’t possible for the genre of work I’ve been doing - Jellyfish 2023 - new work - it needs: osc sources between unity and mediapipe, several helper synths to wrap the mediapipe stuff, the audio, osc mapping functions, osc maps content, osc senders, and gui helpers for some of those things. Even if I simplified it there is easily several thousand lines.

Perhaps a one file approach works well for a particular style of music? And in general, if such considerations should be reflected in ‘opinionated advice for supercollider beginners’?

1 Like

D’n’b producer Cylob used to have a big “do-everything” system in SC2.

Around 2005, I started writing all of my compositional code into Proto objects. Now here, I suppose saying this will trigger several standard rants about “prototypes are awful in SC” but I derive a lot of benefit from this. My live set up loads a lot of scd files but very, very few global variables (master and reverb mixer channels, a one-key hook for control mapping, and a tempo fader, that’s about it).

Now… this might not help beginners that much. But, for example, a few weeks ago when I was preparing for a duo jam, I wanted some vocals to adapt to the tempo and key. So I wrote a Proto process (which includes all buffer loading and unloading, mixing framework management etc.) and a factory object including a small database of audio files and metadata. Then, in the show:

// this loads everything -- \twist is the database key
/make(voxBP:tw(set:\twist));

// and this says "play a different section, on the downbeat, every 8 bars"
/tw = "1";
/tw..section = "[*]::\seq("1234")";
/tw = (main.rest*7);

It is more weight up front to put all of that into a Proto, but what I get out of that is a package that Just Works, and then I don’t have to fight with it again. 18 years of this working method (the live-coding dialect is more recent though), without significant design changes – I’m satisfied that this is working reasonably well.

The complexity level that Jordan is talking about would definitely benefit from objects, whether standard objects or the types of Proto that I’ve been using.

hjh

I absolutely needed to write classes to build the piece I’m working on now! Beyond that its just a joy when developer-me succeeds in providing user-me a pleasant interface! I spent a couple years using vanilla SC before venturing into writing my own classes, and it took a couple years of that before the system became (somewhat!) productive. I have my classes and pieces all in a giant git repo so if I change the classes one day and break old pieces it should be straightforward to wind things back (famous last words!).

1 Like

is supercollider more an instrument or a platform?
i see it more as a platform - one that requires you to create your own idiom

1 Like

So I was just trying change to using load and String:resolveRelative and I don’t really think your criticism holds up due to a subtle detail…

% ./folder/sub_file.scd
~a = "some_sample.wav".resolveRelative ;
s.sync;
~b = "some_sample.wav".resolveRelative ;
% ./main.scd
s.waitForBoot {
   "folder/sub_file.scd".resolveRelative.load;
}

Here ~a does not equal ~b because by the time the server has synced the current executing file has changed.
Now this just flat out doesn’t work with executeFile, which I think is better than sometimes working and sometimes not (just spent half an hour trying to fix this).

Retracted one post because of a/ unnecessary punchiness (apologies for that – it was late and I should have just gone to bed) and b/ factual errors.

In any case, there is a bug with nowExecutingPath and threads.

fileA.scd:

(
fork {
	thisProcess.nowExecutingPath.debug("A1");
	thisThread.executingPath.debug("A1 thread path");

	(thisProcess.nowExecutingPath.dirname +/+ "fileB.scd").load;

	thisProcess.nowExecutingPath.debug("A2");
	thisThread.executingPath.debug("A2 thread path");
};
)

fileB.scd:

thisProcess.nowExecutingPath.debug("B1");
thisThread.executingPath.debug("B1 thread path");

0.5.wait;

thisProcess.nowExecutingPath.debug("B2");
thisThread.executingPath.debug("B2 thread path");

Result:

A1: /home/.../.../fileA.scd
A1 thread path: /home/.../.../fileA.scd
B1: /home/.../.../fileB.scd
B1 thread path: /home/.../.../fileA.scd
B2: /home/.../.../fileA.scd		<<-- this is wrong
B2 thread path: /home/.../.../fileA.scd
A2: /home/.../.../fileA.scd
A2 thread path: /home/.../.../fileA.scd

Yesterday I had said that nowExecutingPath might not be set in context of a thread. AFAICS that’s not true – whenever a thread is awakened, the thread’s executingPath populates nowExecutingPath, and when it yields, nowExecutingPath should be reverted.

The problem here is that load-ing a different file within a thread needs to update the thread’s executingPath, but it doesn’t. (I have a bit of a hacky fix, but it’s probably not a good one… will log it shortly.)

This probably explains the sync problem that you ran into.

I suppose that I can apologize for being unaware of that bug. However, I get a slight feeling that some of the energy in that post is going toward proving me wrong, where a better focus would be solving the problem… just noting it, I won’t go further into that.

hjh

Didn’t realise this was a bug, thought that was just how it works - if it is fixed, using load is far better than the mess I suggested.

I’d have a go at fixing it, but I the routines and thread side of sc is where I am least comfortable, so there might be someone better suited.

The infrastructure is there for a thread to remember where it’s executing. It’s just not consistently updated, which IMO is not realizing the spec properly.

TBH here… in my live setup file, I have var path = thisProcess.nowExecutingPath and then I use path throughout. That was a while ago. I don’t remember if I had tried using thisProcess.nowExecutingPath, and it broke, and then I tried to work around it – or maybe I just assumed “this isn’t going to work in a thread” and preemptively worked around it.

(Now, the path infrastructure doesn’t exist for functions – so, scheduled functions don’t have any awareness of the path in which they were defined. I don’t think this is a big problem because standalone functions don’t persist their status in the way that routines do, and if you really need it, the workaround isn’t difficult.)

It’s now at thisThread's executingPath is not updated when doing `executeFile` in a Routine · Issue #6028 · supercollider/supercollider · GitHub – eventually it will get some attention.

hjh

1 Like

damn, I feel attacked…

… are you really care?

Good article. Taking inspiration from cool pieces of hardware is great, as it helps you to think about problems in a general way.

I got pretty overwhelmed when starting out with SuperCollider and I was not new to programming or synthesis or music production.

So my tip is more for casual self-learners, that just want to hack something together to make sick tunez after work, rather than the lucky few that get to invest much more time.

My tip is (especially if you feel overwhelmed): you don’t have to go ‘all in’ on Supercollider, when starting out.

  • Instead of synthesis exclusively in SuperCollider, you could record VSTs and explore triggering samples from within SuperCollider
  • Just explore midi sequencing and CC
  • If understanding Groups and Busses is not quite sticking yet, consider just routing out to a DAW (or quarks as mentioned above).

There are many other ideas for using Supercollider in a limited way. The point is not to sweep difficult concepts under the rug, but to try and learn them incrementally while still getting your hands dirty writing code and enjoying the new capability. As ideas get more ambitious, there’s more motivation to take on the trickier concepts.

For programmers: one thing I did was cherry pick problems from coding sites like code wars or exercism and solve them in sclang. That was a fun way to explore the language and get a feel for it. Instead of just wishing it had feature x from lang y.

You mentioned Synth secrets, which I quite like. There’s also this old nord modular book - you’re probably aware of it, though I’m not sure if it’s a good addition to your article.

I experienced something similar. I was losing motivation to write supercollider code after writing code all day at work. My productivity increased once I stopped caring about the usual things when developing in a professional context.

My music code doesn’t need to scale, it doesn’t need to be readable or maintained by anyone else, it doesn’t need to be DRY, etc etc. It just needs to sound good! If I were collaborating, then ok sure, I would have a different approach.

Just discovered this lately. Certainly a lot of very useful recommendations! Some of them with a strong opinion – I might not agree with – but that’s ok.
I especially like the emphasis on practice, keeping things technically clean and simple and – at the end – LISTENING to the results. Having said that, sometimes curiosity inevitably leads you to things that are not simple … :slight_smile:

One side-remark concerning the Amplitude UGen you are mentioning in the collection of SuperCollider Tips: it’s not only the default settings, it’s mathematically wrong in more than one regard – a bad bug, as Julian described it (my example of June 4, 2020 is clearer than my first post). However, Amplitude is still of practical usage:

1 Like

I can only partially confirm what you have been experiencing. Indeed, I’ve developed several classes for miSCellaneous_lib that I have never used musically in serious projects (E.g.: Sieve / Psieve patterns, ironically, I know that others did use them). However, I’ve used several classes intensely over a long period of time: VarGui is serving my GUI needs since 2007 or so for all of my projects. I’ve used it in combination with PLx Pattern classes for Pattern-based granulation very often.
What also happens quite often is that classes turn out to be useful with a delay of several years. I’ve used PbindFx – which took me three years to develop – for the first time in a larger project last year.

Yes so true. That’s my preference - for curiosity to lead me towards complexity, rather than racing towards it, as I have in the past. I guess there are some parallels there with premature optimization.

Edit: And yes your miSCellaneous_lib is super cool and very useful! Thank you.