How to load startup.scd path automatically on boot?

how to load startup.scd path automatically on boot? i have the file, it gets interpreted without errors. i can execute it with load command, but i want to add that instruction to the supercollider boot sequence. how can i do that? cheers

i figured out. i have to edit the server class. i just don’t know exactly how. this is what i guessed

boot { | startAliveThread = true, recover = false, onFailure |
    (...)
	statusWatcher.doWhenBooted({
		statusWatcher.serverBooting = false;
		this.bootInit(recover);
		Platform.userConfigDir +/+ "startup.scd";
	}, onFailure: onFailure ? false);
    (...)
}

however i have supercollider to discard changes automatically

As far as I know, the complete code blocks in the β€˜startup.scd’ file are automatically evaluated as soon as sclang is booted.

Code requiring server boot should be written inside s.waitForBoot { ... }.

This behaviour is independent of the IDE, as I observed it is the same when using VSCode and Vscodium.

So I think checking the server boot status is usually not required.

Could you refer to the following GitHub PR?

Additionally, if you want to register post-boot actions for the server so that they will run for any boot request (including ctrl- / cmd-B), there are:

(
ServerBoot.add { |server|
	"this will happen after the server boots but before groups are created".postln;
	server.dumpOSC(1);
};

ServerTree.add { |server|
	"this happens after default groups are created and synthdefs are sent".postln;
};
)

s.boot;

That’s potentially risky – for one thing, your modifications would be lost when you upgrade the SC version.

It’s also unnecessary, because SC already has user-facing bootup hooks.

BTW startup.scd should be for language startup actions. You shouldn’t need to repeat language startup actions when booting a server. I think you should keep your server boot stuff in a separate file. Then, in your language startup.scd:

ServerTree.add { |server|
	thisProcess.interpreter.executeFile(
		"/path/to/server-boot.scd",
		server
	)
};

Now any server boot process will load that file. Write arg server; at the top of the file so that the script knows which server object booted.

hjh

2 Likes

This snippet, based on @jamshark70’s idea from another discussion, is good for demonstration.

(

~remapConfig = IdentityDictionary[
    0  ->  1,
    1  ->  2,
    2  ->  3,
    3  ->  4,
    4  ->  5,
    5  ->  6,
    6  ->  7,
    7  ->  8,
    8  ->  9,
    9  -> 12,
    10 -> 13,
    11 -> 14,
    12 -> 15,
    13 -> 16,
    14 -> 17
];


~generateRemapSynthDef = { |numInputChannels, numOutputChannels|
    SynthDef(\remapOutputs, {
        var sig = In.ar(0, numInputChannels);  
        var remapped = Array.fill(numOutputChannels, { Silent.ar(1) });  

        ~remapConfig.keysValuesDo { |src, dest|
            if (src < numInputChannels and: { dest < numOutputChannels }) {
                remapped[dest] = sig[src];
            }
        };

        ReplaceOut.ar(0, remapped);
    }).add;
};

~generateRemapSynthDef.(16, 18);  
)

(
~createRemapSynth = {
    ~remapSynth = Synth(\remapOutputs, target: s.defaultGroup, addAction: \addAfter);
};

ServerTree.add( ~createRemapSynth);

)

Hi everyone,

I’m currently trying to run some older code that relies on Server.waitForBoot. To make this work smoothly in certain contexts, I’ve been experimenting with modifications to Server.sc.

I know there’s already an open PR addressing this exact issue, and I really appreciate the effort that went into it! Thank you especially for pushing it forward and testing it so quickly.

I’m not personally building nightly versions of SuperCollider on Windows, but I’m very eager to see this change land in an official release. This behavior was actually shown to me years ago (around 2013) by Bas Gebbing at Sonology), and it’s always stuck with me as something that should β€œjust work” out of the box.

I’m really looking forward to the day this becomes part of the standard distribution.

Any idea when the next official SuperCollider release might be coming?

Thanks again for all the hard work, Cheers, T.

s.waitForBoot({

// ──────────────────────────────────────────────────────────────
// 1. Start the four Ndefs (stereo output, nice crossfading)
// ──────────────────────────────────────────────────────────────
[\a, \b, \c, \d].do { |name|
    Ndef(name).play.fadeTime_(2);   // 2-second crossfade feels great
};

// ──────────────────────────────────────────────────────────────
// 2. Define the three banks in a compact way
// ──────────────────────────────────────────────────────────────
~bankTest = [
    { WhiteNoise.ar ! 2 },
    { BrownNoise.ar ! 2 },
    { PinkNoise.ar  ! 2 },
    { GrayNoise.ar  ! 2 },
    { ClipNoise.ar  ! 2 }
];

// Helper that returns a random LFD noise ugen (mono)
~rndLFD = {
    [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose
        .ar( rrand(1, 30) * [1, 10].choose )   // normal or MAX version
};

// Normal LFD bank (5 identical entries β†’ just pick random each time)
~bankLFD = 5.collect {
    { [~rndLFD.(), ~rndLFD.()] }   // always stereo, random each evaluation
};

// MAX version (10Γ— faster rates)
~bankMAX = 5.collect {
    {
        var freq = rrand(1, 30) * 10;
        [
            [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose.ar(freq),
            [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose.ar(freq)
        ]
    }
};

// ──────────────────────────────────────────────────────────────
// 3. Master random picker (chooses bank β†’ chooses synth)
// ──────────────────────────────────────────────────────────────
~pick = {
    [~bankTest, ~bankLFD, ~bankMAX].choose.choose.value;
};

// ──────────────────────────────────────────────────────────────
// 4. Handy functions you can call anytime
// ──────────────────────────────────────────────────────────────
~one   = { Ndef( [\a,\b,\c,\d].choose, ~pick ).play; };
~all   = { [\a,\b,\c,\d].do { |n| Ndef(n, ~pick).play; } };
~stop  = { Ndef.clear(4) };                 // gentle 4-second fade-out of all
~panic = { Ndef.all.do(_.end(0)) };         // hard kill if needed

"β–Ί Noise drone system ready!
β†’ ~one.()     – change one random layer
β†’ ~all.()     – change everything
β†’ ~stop.()    – gentle stop
β†’ ~panic.()   – emergency kill".postln;

});

by the way, this is how my current supercollider startup.scd looks like, in a quick and short reply to prko. all the code was done by me from scratch but i revamped it with grok

I think you could use this code when removing () enclosing { ... } as follows:

s.waitForBoot {

// ──────────────────────────────────────────────────────────────
// 1. Start the four Ndefs (stereo output, nice crossfading)
// ──────────────────────────────────────────────────────────────
[\a, \b, \c, \d].do { |name|
    Ndef(name).play.fadeTime_(2);   // 2-second crossfade feels great
};

// ──────────────────────────────────────────────────────────────
// 2. Define the three banks in a compact way
// ──────────────────────────────────────────────────────────────
~bankTest = [
    { WhiteNoise.ar ! 2 },
    { BrownNoise.ar ! 2 },
    { PinkNoise.ar  ! 2 },
    { GrayNoise.ar  ! 2 },
    { ClipNoise.ar  ! 2 }
];

// Helper that returns a random LFD noise ugen (mono)
~rndLFD = {
    [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose
        .ar( rrand(1, 30) * [1, 10].choose )   // normal or MAX version
};

// Normal LFD bank (5 identical entries β†’ just pick random each time)
~bankLFD = 5.collect {
    { [~rndLFD.(), ~rndLFD.()] }   // always stereo, random each evaluation
};

// MAX version (10Γ— faster rates)
~bankMAX = 5.collect {
    {
        var freq = rrand(1, 30) * 10;
        [
            [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose.ar(freq),
            [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose.ar(freq)
        ]
    }
};

// ──────────────────────────────────────────────────────────────
// 3. Master random picker (chooses bank β†’ chooses synth)
// ──────────────────────────────────────────────────────────────
~pick = {
    [~bankTest, ~bankLFD, ~bankMAX].choose.choose.value;
};

// ──────────────────────────────────────────────────────────────
// 4. Handy functions you can call anytime
// ──────────────────────────────────────────────────────────────
~one   = { Ndef( [\a,\b,\c,\d].choose, ~pick ).play; };
~all   = { [\a,\b,\c,\d].do { |n| Ndef(n, ~pick).play; } };
~stop  = { Ndef.clear(4) };                 // gentle 4-second fade-out of all
~panic = { Ndef.all.do(_.end(0)) };         // hard kill if needed

"β–Ί Noise drone system ready!
β†’ ~one.()     – change one random layer
β†’ ~all.()     – change everything
β†’ ~stop.()    – gentle stop
β†’ ~panic.()   – emergency kill".postln;
}

As for me, I would enclose the whole code block with () as follows:

(
s.waitForBoot {

// ──────────────────────────────────────────────────────────────
// 1. Start the four Ndefs (stereo output, nice crossfading)
// ──────────────────────────────────────────────────────────────
[\a, \b, \c, \d].do { |name|
    Ndef(name).play.fadeTime_(2);   // 2-second crossfade feels great
};

// ──────────────────────────────────────────────────────────────
// 2. Define the three banks in a compact way
// ──────────────────────────────────────────────────────────────
~bankTest = [
    { WhiteNoise.ar ! 2 },
    { BrownNoise.ar ! 2 },
    { PinkNoise.ar  ! 2 },
    { GrayNoise.ar  ! 2 },
    { ClipNoise.ar  ! 2 }
];

// Helper that returns a random LFD noise ugen (mono)
~rndLFD = {
    [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose
        .ar( rrand(1, 30) * [1, 10].choose )   // normal or MAX version
};

// Normal LFD bank (5 identical entries β†’ just pick random each time)
~bankLFD = 5.collect {
    { [~rndLFD.(), ~rndLFD.()] }   // always stereo, random each evaluation
};

// MAX version (10Γ— faster rates)
~bankMAX = 5.collect {
    {
        var freq = rrand(1, 30) * 10;
        [
            [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose.ar(freq),
            [LFDNoise0, LFDNoise1, LFDNoise3, LFDClipNoise].choose.ar(freq)
        ]
    }
};

// ──────────────────────────────────────────────────────────────
// 3. Master random picker (chooses bank β†’ chooses synth)
// ──────────────────────────────────────────────────────────────
~pick = {
    [~bankTest, ~bankLFD, ~bankMAX].choose.choose.value;
};

// ──────────────────────────────────────────────────────────────
// 4. Handy functions you can call anytime
// ──────────────────────────────────────────────────────────────
~one   = { Ndef( [\a,\b,\c,\d].choose, ~pick ).play; };
~all   = { [\a,\b,\c,\d].do { |n| Ndef(n, ~pick).play; } };
~stop  = { Ndef.clear(4) };                 // gentle 4-second fade-out of all
~panic = { Ndef.all.do(_.end(0)) };         // hard kill if needed

"β–Ί Noise drone system ready!
β†’ ~one.()     – change one random layer
β†’ ~all.()     – change everything
β†’ ~stop.()    – gentle stop
β†’ ~panic.()   – emergency kill".postln;
}
)

I believe you don’t need to change any SC files.

There are, however, some details missing from this request, such that, in its current form, nobody can act on it.

β€œβ€¦ there’s already an open PR…” Which PR?

β€œβ€¦ modifications to Server.sc…” Which modifications? (There is one modification listed in an earlier post, but that was β€œthis doesn’t work, how to fix it?” which wouldn’t be suitable for a PR. So I have to assume you mean a different modification, but, no other detail is given so I can’t begin to guess.)

β€œβ€¦ something that should β€˜just work’ out of the box.” Feature requests are, of course, welcome, but there isn’t anything concrete to evaluate here. What should β€œjust work”?

There are already two persistent server boot hooks:

  • ServerBoot – run a function immediately after the server is ready for commands, but before default groups are up
  • ServerTree – run a function after default groups have been created

The purpose of these is to make it easy for users to customize the server boot sequence without needing to modify the class library.

So there’s a claim in this thread that the existing mechanisms don’t meet your requirement. That’s a fair claim to evaluate, but no specifics have been provided about what it is you need to do that isn’t supported by ServerBoot or ServerTree. Without anything concrete to evaluate, there’s no action to be taken.

hjh

Hi @jamshark70 , @prko

I’ve noticed that the documentation could be more comprehensive. Perhaps a resource/blog post or a PR could highlight some β€œreal-world” problems that ServerBoot and ServerTree effectively solve.

Thank you for sharing your opinion.
I attempted to add some examples relating to ServerBoot and ServerTree, but the developers did not accept them:

However, that pull request is not directly relevant to the issue under discussion in this thread.
I would be grateful if you could suggest more concrete improvements to the following PR:
Add Guide to .scd File Evaluation and Execution: AutoRun, Load, CLI Methods, etc by prko Β· Pull Request #7272 Β· supercollider/supercollider Β· GitHub.

A direct change would be even more welcome!

1 Like

Sure, there’s plenty of room for that. These help files are indeed a bit thin.

hjh