Specification and base API of ScalaCollider UGens, as well as a core library of generated UGen classes.
This project is (C)opyright 2008–2021 by Hanns Holger Rutz. All rights reserved. All sub projects released under the GNU LGPL v2.1+, except for the specification which is released under a BSD-style license. All code comes with absolutely no warranties. To contact the author, send an e-mail to
contact at sciss.de.
speccontains an XML specification of the standard SuperCollider UGens, suitable for synthesising ScalaCollider UGen classes or other meta data purposes
apicontains the base classes for ugens, graph elements, ugen and synth graphs.
gengenerates the source codes of the
coreproject from the descriptions provided by the
corecontains ScalaCollider classes for the standard UGens
All artifacts are published to Maven Central, and are available as follows:
"de.sciss" % "scalacolliderugens-spec" % v "de.sciss" %% "scalacolliderugens-api" % v "de.sciss" %% "scalacolliderugens-core" % v "de.sciss" %% "scalacolliderugens-plugins" % v
The current stable version
spec contains the XML meta data,
api contains basic types without specific UGens,
core contains the standard UGens included with SuperCollider, and
plugins will include the third-party plugins managed by the sc3-plugins project (still incomplete).
The project builds with sbt against Scala 2.13, 2.12, Dotty (JVM) and Scala 2.13 (JS). The last version to support Scala 2.11 was 1.19.5.
The synthetic UGen sources are inside directories
plugins/gen. Since v1.20.1, when the XML specifications are changes, these must be recreated, by running
sbt gen/ugen! The synthetic sources are currently checked into git, thus you do not necessarily have to regenerate these files.
To compile all sources, run
Please see the file CONTRIBUTING.md
generating additional UGen class files
The UGen descriptions reside in XML files. The
spec sub project contains files for the standard UGens included with a plain SuperCollider installation. You will need to create additional XML files if you wish to compile sources for third party UGens.
To synthesize the source code for a given UGen description XML file, run as follows:
$ sbt ... > project gen > run -d path/to/scala/source/output path/to/descriptions.xml
The generated source files then need to be compiled against
If you want to contribute UGen descriptions, there are two existing sbt sub-projects to consider:
corecreates class files for the UGens included in the standard SuperCollider distribution. If you are not editing an existing XML file, you need to ensure a new XML file is included in
pluginscreates class files for the UGens included in the sc3-plugins umbrella project. Again, if you are not editing an existing XML file, you need to ensure a new XML file is included, this time in the list
- if you are planning to contribute the description of a plugin that is neither in the standard UGens nor in the sc3-plugins project, please consult first where those classes should be published. Most likely, we will add another sbt sub-project similar to the
pluginsproject. If you look at its code, you will see that you have to change the call to
runUGenGeneratorso that instead of
args = "--plugins" :: Nilyou provide an explicit list of paths to the XML files you wish to generate sources from.
Please have a good look at the current XML files and try to follow the documentation style used there. In particular
- try to be precise about the technical description of the behaviour of the UGen and its arguments. Try to think of the typical case where you would consult the API doc of the UGen. Which are the behavioural aspects you would want to look up, such as initialisation conditions, precise triggering behaviour, interaction of parameters, differences to related UGens.
- each argument should be described in terms of its behaviour, the physical units (if any), the allowed or expected value ranges, and corner cases.
- ideally, each UGen should be documented along with a concise example (in the XML file).
format of ugen XML descriptions
There is no DTD yet. But the structure of the XML file is as follows:
<ugens revision="<num>"> <ugen name="UGenName" [ ugenAttrs ]> <rate name="RateName" [ implied="true" [ method="MethodName" ]]> [ <arg ... /> ] </rate> <rate ... /> [ <output ... /> ] <arg name="ArgumentName" [ argAttrs ]> [ <doc>Argument description</doc> ] </arg> <arg ... /> [ <doc> <text>UGen description</text> [ <example name="description"> Example code demonstrating the UGen </example> <example ... /> ] [ <see>ugen.RelatedUGenName</see> <see ... /> ] </doc> ] </ugen> [ <ugen ... /> ] </ugens>
All UGens within one file are considered to be part of that particular
.scx plugin. Their synthesized classes will also be grouped in a file by that name.
UGen Attributes (
ugenAttr) are boolean flags (all false by default) which can be set to characterize a UGen:
|Attribute Name||Meaning when value is
||UGen reads from a bus||
||UGen writes to a bus||
||UGen reads audio buffer data||
||UGen overwrites audio buffer data||
||UGen reads from an FFT buffer||
||UGen writes to an FFT buffer||
||UGen sets a 'done flag'||
||UGen has another side effect, for example causing a 'done-action', sending OSC commands, or printing to the console||
||UGen depends on random seeding||
||Each UGen is otherwise individual, even with identical inputs||Demand UGens advance their inputs|
||A helper element that is not a genuine UGen itself||
||A UGen that might be optimized at runtime to other UGens (allows to skip rate specification)||
||Manually written source code is provided||
||UGen that cannot be fully represented in the spec. For example, it has hidden or currently not represented argument types||
||Client facing class name differing from UGen||
Part of this information is used by ScalaCollider when building the UGen graph. For example, subtrees which do not have any side effects are automatically removed. UGens which have side effects are those for which either of the following flags is set:
side-effect. Furthermore, multiple occurrences of UGens which are functionally equivalent are collapsed. UGens are functionally not equivalent if either of the following flags is set: any of the side effects | any of the resource readers |
indiv. That is to say, if there are two
WhiteNoise UGens, they are functionally distinct by definition and will thus not be collapsed. The same is true for two
Out UGens, even if their inputs are the same, as they have accumulative side effects on the bus to which they write. On the other hand, two
SinOsc UGens with the same frequency and rate inputs are functionally equivalent and thus one can be replaced for the other.
More flags and meta data are planned in future version, e.g. oscillator signal ranges, filter coefficients.
The possible rate names are
"demand". Each supported rate should have its own element. There are three extra attributes,
implied says that the UGen not only has exactly one supported rate (an exception is thrown if you have a UGen with multiple rate elements and an
implied attribute), but that this a natural precondition for the type of UGen. That way, the
case class for that UGen does not carry a
rate argument, but mixes in a trait which provides it. As a consequence, there is no argument for the rate when using pattern matching against that UGen. For example,
K2A makes only sense at audio rate,
A2K makes only sense at control rate,
Pitch make only sense at audio rate. Using this attribute, we have
case class K2A(in: GE) (with mixin
AudioRated) instead of the redundant
case class K2A(rate: Rate, in: GE).
Be very careful with this attribute, it should not be used if another rate could be added in a future SuperCollider version, as this would break binary compatibility. This is why
implied has been removed from
DiskIn, for example (there is no reason, why
DiskIn could not support control rate reading in the future).
The second attribute,
method, builds up
implied and requires that
implied has been specified. It states that instead of the default method name in the companion object—
ir for scalar rate,
kr for control rate,
ar for audio rate, and
dr for demand rate—an alternative method name is used. The method name is typically
apply, so that instead of
FFT.kr(buf, sig) you have to write
FFT.apply(buf, sig) or short
FFT(buf, sig), which is more convenient.
method-alias adds an additional method for the rate. An example is
IFFT which specifies
<rate name="audio" method-alias="apply"/>. This means the default method
ar is created, plus an
apply method as an alias.
||Default expression for the argument. This can be either a number literal or a special string such
||Argument type when it is not
||Constrains the supported rate for this argument. The only values presently recognized are
The following table lists the allowed
type values, and corresponding ways of defining default values. If the default value is unambiguous, the type is automatically inferred, e.g. using
default="high" implies a
type="trig". If the type and default value are incompatible, the parser will throw an exception.
|Type name||Description||Example defaults|
||Generic graph element||
||Graph element used as integer||
||String converted to variadic float constants||
||Bus index||no default allowed|
||Buffer identifier||no default allowed|
||FFT buffer phase chain signal||no default allowed|
||Trigger signal (transition <= 0 to >0)||
||Off/On signal (zero versus non-zero)||
||Gating signal (open above zero)||
||Synthetic multiplier input||
||UGen which sets a done flag||no default allowed|
||Static integer (no graph element)||
A special default value
"nyquist" can be used which is understood as
SampleRate.ir/2. Note that expressions such as
"60.midicps" have been currently disallowed for simplicity and language neutrality.
The following three argument attributes have boolean values, and are
"false" by default:
|Attribute Name||Meaning when value is
||Indicates an argument which expands over multiple UGen inputs.||
||Must be combined with
Arguments should be chosen careful not to conflict with methods available on
GEOps. This is the reason, why various arguments which are named
rate in SCLang have been renamed for example to
freq etc. It is recommended to take a look at the naming of the arguments in the default plugins (rather than relying on the naming in SCLang which is often unreflected and irregular) and try to reuse them whenever possible, and to be as consistent as possible with abbreviations. Care is also needed with the default values. There are some default values in SCLang which are insensible, while other useful defaults are missing. The aim is not to provide default values for every possible argument, but to require to fill in arguments for which defaults do not make sense.
For some UGens, the actual positions of the arguments as they are coded in the underlying Plugin are either unintuitive (e.g. with respect to argument priority), inconvenient (e.g. with respect to default values), or irregular (e.g. different from an otherwise very similar other UGen). In those cases you are permitted to change the argument order as it is presented to the ScalaCollider user. To make sure the arguments are properly wired in the resulting SynthDef, explicit argument positions must be given.
If a UGen's arguments do not have
pos attributes, they are considered in the order in which they appear in the XML file. Otherwise, the order of appearance in the XML file corresponds with the order in the underlying Plugin, whereas the values of the
pos attributes specify the positions as presented to the user (counting from zero). Please read the previous sentence very carefully, as a common mistake is to falsely believe the correspondence to be the other way around.
As an example, consider
XOut which has the unintuitive argument order of bus, followed by cross-fade level, followed by input signal. Compare this to
Out which has the two arguments of bus, followed by input signal. We decided to make the
XOut arguments appear to the user in the order of bus, then input signal (just like
Out), then followed by the distinguishing parameter of the cross-fade level. Thus we assign
pos="1" to the
in argument and
pos="2" to the
xfade argument, so they switch their positions. To minimize mistakes, ScalaCollider-UGens requires that we also add
pos="0" to the
bus argument, even if that does not affect its final position. The whole UGen specification thus becomes:
<ugen name="XOut" writes-bus="true"> <no-outputs/> <rate name="audio"> <arg name="in" rate="ugen"/> </rate> <rate name="control"/> <arg name="bus" pos="0"/> <arg name="xfade" pos="2"/> <arg name="in" variadic="true" pos="1"/> <doc warn-pos="true"/> </ugen>
Note how the attribute
warn-pos="true" was added to the
doc element. This makes Scaladoc add an extra note to alert the user of the change in argument order. This is particularly important, as it may create confusion when coming from SCLang. It is recommended to apply argument reordering only after careful consideration, and to abstain from them when in doubt.
Rate Specific Argument Settings
Sometimes it is necessary to change the default value or the description of an argument with respect to the rate at which the UGen runs. And sometimes the rate constraint for an argument only applies to particular rates at which the UGen runs. In this case, it is permitted to embed an auxiliary
arg element inside the
rate element. This auxiliary
arg element must have a corresponding element in the outer scope (inside the
ugen element). Their correspondence is established by using the same
name attribute, and the auxiliary element may provide an additional
rate attribute and may contain an additional
As an example for different default values, here is the full text of
<ugen name="LeakDC"> <rate name="control"> <arg name="coeff" default="0.9"/> <!-- provide a default value for the `kr` method --> </rate> <rate name="audio"> <arg name="coeff" default="0.995"/> <!-- provide a default value for the `ar` method --> </rate> <arg name="in" rate="ugen"/> <arg name="coeff"/> <!-- the outer argument must still be provided --> </ugen>
An example of restricting the argument's rate only in certain cases is
<ugen name="Out" writes-bus="true"> <no-outputs/> <rate name="audio"> <arg name="in" rate="ugen"/> </rate> <rate name="control"/> <rate name="scalar"/> <arg name="bus"/> <arg name="in" variadic="true"/> </ugen>
Here, the "outer" definition of argument
in says that the argument is a multi-channel argument, but it does not enforce a particular rate. Only for the case that
Out is run at audio rate, the auxiliary entry for
in enforces that
in in this case must run at the same rate as the UGen (thus audio rate, too).
By default, the UGen is considered to have one monophonic output. All other UGens must explicitly contain either a
<no-outputs/> element, or one or more
<output ... /> elements. An output element may have a
type attribute, and one element may have a
variadic="<id>" attribute, where
<id> is the name of the input argument determining the number of channels. A
<doc> element may be nested inside a
<output> node. Examples:
The description text for arguments is the text inside the argument's
<doc></doc> element. The description text for the UGen is inside the
<text></text> element inside the
<doc></doc> element. In each case, standard Scaladoc formatting is allowed. Cross-links are provided through any number of
Please follow carefully the style of the descriptions used for the standard UGens. They adhere mostly to Javadoc style practice, and not so much to the more colloquial style of SCLang docs. The purpose here is not to include lengthy examples, but to be technically precise in the meanings of the argument values and the exact functioning of the UGen, if possible covering corner cases, providing details about underlying formulas, phase behavior of oscillators, typical ranges and scale.
Whenever the argument order has been significantly changed from the SCLang counterpart, the UGen's
doc element should contain the attribute
warn-pos="true" which will create a special highlight in the Scala docs to alert the reader of this change.
In order to register all necessary deserialization factories, UGens may define adjunct types. An adjunct is defined by a reader class that implements
ProductReader, and a number of product prefixes that are returned from the reader. For instance, the
UnaryOpUGen each define their own
Op types which have to be serialized and deserialized. In these cases, there is one reader and one returned type:
<adjunct reader="UnaryOpUGen.Op" self="true"/>
The reader object is
UnaryOpUGen.Op and the prefixes supported solely consists of itself (it will be automatically translated to
UnaryOpUGen$Op). Any additional types deserialized can be added within the
adjunct element as
prefix element with
<adjunct reader="Env.Curve"> <prefix name="Env$Curve$Apply" /> <prefix name="Env$Curve$Const" /> </adjunct>