Skip to main content

nanosynth

nanosynth is a Python package that embeds SuperCollider's libscsynth and supernova synthesis engines in-process using nanobind. It makes it possible to define SynthDefs in Python, compile them to SuperCollider's SCgf binary format, boot the embedded audio engine, and control it via OSC -- all without leaving Python.

Features

  • Self-contained with embedded synthesis engines -- both libscsynth and supernova run in-process as Python extensions (vendored and built from source), no separate process required. supernova is SuperCollider's parallel DSP engine -- it distributes independent synth nodes across CPU cores via ParGroups, while scsynth runs everything on a single audio thread

  • High-level Server class -- boot/quit lifecycle, node ID allocation, SynthDef dispatch, buffer management, OSC reply handling, and convenience methods (synth, group, par_group, free, set). Context manager support and managed_synth()/managed_group()/managed_par_group()/managed_buffer() for automatic resource cleanup. Same API for both engines -- just pass protocol=EmbeddedSupernovaProtocol() to use supernova

  • Pythonic SynthDef builder -- define UGen graphs using a context manager and operator overloading, compiled to SuperCollider's SCgf binary format

  • 340+ UGens -- oscillators, filters, delays, noise, chaos, granular, demand, dynamics, panning, physical modeling, reverb, phase vocoder, machine listening, stochastic synthesis, and more

  • Rich operator algebra -- 43 binary and 34 unary operators on all UGen signals, including arithmetic, comparison, bitwise, power, trig, pitch conversion (midicps/cpsmidi), clipping (clip2/fold2/wrap2), and more. Compile-time constant folding and algebraic optimizations

  • Non-real-time (NRT) rendering -- Score class for offline audio rendering to WAV/AIFF files without audio hardware. Timestamped OSC commands are serialized and rendered by the embedded engine

  • Bus allocation -- Bus proxy class with Server.audio_bus(), Server.control_bus(), managed_audio_bus(), managed_control_bus(). Eliminates hardcoded magic bus numbers in effect chains. int() compatible for passing as synth parameters

  • Pattern sequencing -- Pbind, Pseq, Prand, Pwhite, Pseries, Pgeom, Pchoose, Pn, Pconst, Rest, Clock, and Player for musical event scheduling. Patterns are reusable iterables; Pbind produces event streams that drive synth creation with automatic gate release. Clock provides tempo-driven playback with drift-free scheduling

  • MIDI input -- MidiIn class for receiving MIDI from hardware controllers (via embedded RtMidi: CoreMIDI on macOS, ALSA on Linux, WinMM on Windows). Parsed message types (NoteOn, NoteOff, ControlChange, PitchBend) with handler registration. High-level helpers: midi_note_map() for polyphonic note-to-synth mapping, midi_cc_map() for CC-to-parameter control

  • NodeProxy / Ndef -- live coding with hot-swappable synth definitions. NodeProxy owns a private audio bus, a source synth (with ASR envelope for crossfade), and a monitor synth. Swap the source seamlessly while audio plays. Ndef is a global named proxy registry for concise live-coding workflows

  • Server recording -- Server.record(path) captures real-time audio output to WAV/AIFF via DiskOut. stop_recording() finalizes the file. Configurable channel count, bus, and format

  • Buffer management -- alloc_buffer, read_buffer, write_buffer, free_buffer, zero_buffer, close_buffer, and context managers for automatic cleanup

  • Direct numpy buffer exchange -- get_buffer_data / set_buffer_data / alloc_buffer_from_array copy samples straight between a numpy array and a buffer's in-process memory (a memcpy, no OSC round-trip and no datagram-size limit). This is the payoff of running the engine in-process: cheap two-way data transfer with the numpy/scipy stack that an out-of-process SuperCollider client cannot match. Requires nanosynth[numpy]

  • Introspection & control -- status() (CPU, sample rate, node/synth/group/ugen counts), version(), query_tree() (the live node graph as a nested NodeInfo tree, optionally with control values), dump_tree(), and reset() (panic: free all nodes, clear the scheduler, recreate the default group)

  • Node lifecycle notifications -- enable_notifications() then on_node(callback) to observe /n_go//n_end//n_off//n_on//n_move as NodeEvents, or Synth.wait_free() / wait_for_node_free(id) to block until a node ends -- the only way to know when a self-freeing DoneAction synth has actually finished

  • Reply handling -- bidirectional OSC communication with the engine: persistent handlers (on/off), blocking one-shot waits (wait_for_reply), and send-and-wait (send_msg_sync)

  • SynthDef graph introspection -- SynthDef.graph() returns a structured DAG of UGenNode/UGenInput NamedTuples for programmatic traversal. SynthDef.to_dot() exports to Graphviz DOT format

  • Envelope system -- Envelope class with factory methods (adsr, asr, linen, percussive, triangle) and the EnvGen UGen

  • OSC codec -- pure-Python OscMessage/OscBundle encode/decode with optional C++ acceleration via nanobind

  • @synthdef decorator -- shorthand for defining SynthDefs as plain functions with parameter rate/lag annotations

  • Full type safety -- passes mypy --strict, complete type annotations throughout

Requirements

  • Python 3.10+

  • uv (package manager)

  • For embedded engines: SuperCollider 3.14.1 (both scsynth and supernova), libsndfile, and PortAudio are vendored and built from source automatically. Audio backend: CoreAudio on macOS, PortAudio (ALSA) on Linux, PortAudio (WASAPI) on Windows -- no system-level audio dependencies beyond the compiler toolchain.

Installation

pip install nanosynth

Or build from source:

# Editable install with embedded scsynth + supernova
uv pip install -e .

# Build wheel (incremental -- reuses cmake build cache in build/)
make build

# Install without supernova (scsynth only)
uv pip install -e . -C cmake.define.NANOSYNTH_EMBED_SUPERNOVA=OFF

# Install without any audio engine (OSC codec + SynthDef compiler only)
uv pip install -e . -C cmake.define.NANOSYNTH_EMBED_SCSYNTH=OFF -C cmake.define.NANOSYNTH_EMBED_SUPERNOVA=OFF

Quick Start

Run the Audio Demos

make demos            # scsynth demos
make demos-supernova  # supernova demos

Define a SynthDef and Play It

The Server class manages the embedded engine lifecycle. Define a SynthDef, boot the server, and play:

import time
from nanosynth import Options, Server
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.synthdef import DoneAction, SynthDefBuilder
from nanosynth.ugens import Out, Pan2, SinOsc

# Define a SynthDef
with SynthDefBuilder(frequency=440.0, amplitude=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["frequency"])
    sig = sig * builder["amplitude"]
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.1, sustain_time=1.8, release_time=0.1),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env
    Out.ar(bus=0, source=Pan2.ar(source=sig))

synthdef = builder.build(name="sine")

# Boot the server, send the SynthDef, create a synth
with Server(Options(verbosity=0)) as server:
    synthdef.send(server)
    time.sleep(0.1)

    node = server.synth("sine", frequency=440.0, amplitude=0.3)
    print(f"Playing 440 Hz sine (node {node}) for 2 seconds...")
    time.sleep(2.0)
    server.free(node)

# Engine shuts down automatically on context exit

Or use SynthDef.play() to send and create a synth in one call:

with Server() as server:
    node = synthdef.play(server, frequency=880.0, amplitude=0.2)
    time.sleep(2.0)

Managed Nodes (Automatic Cleanup)

managed_synth() and managed_group() create nodes that are automatically freed on context exit, even if an exception occurs:

import time
from nanosynth import Server

with Server() as server:
    synthdef.send(server)
    time.sleep(0.1)

    with server.managed_synth("sine", frequency=440.0, amplitude=0.3) as node:
        print(f"Playing node {node}...")
        time.sleep(2.0)
    # node freed automatically here

    # Group multiple voices and free them together
    with server.managed_group(target=1) as group:
        server.synth("sine", target=group, frequency=261.63, amplitude=0.2)
        server.synth("sine", target=group, frequency=329.63, amplitude=0.2)
        server.synth("sine", target=group, frequency=392.00, amplitude=0.2)
        time.sleep(2.0)
    # entire group freed here

Effect Chains with Bus Allocation

Use AddAction to control node execution order and audio_bus() to allocate private buses for effect routing -- no more hardcoded magic bus numbers:

import time
from nanosynth import AddAction, Options, Server

with Server(Options(verbosity=0)) as server:
    src_def.send(server)
    delay_def.send(server)
    time.sleep(0.1)

    # Allocate a private bus for routing source -> effect
    with server.managed_audio_bus(2) as fx_bus:
        # Source group executes first, effect group after
        src_group = server.group(target=1, action=AddAction.ADD_TO_HEAD)
        fx_group = server.group(target=int(src_group), action=AddAction.ADD_AFTER)

        # Effect reads from the allocated bus, writes to hardware output
        server.synth("comb_delay", target=int(fx_group),
                     in_bus=float(int(fx_bus)), delay_time=0.375, mix=0.4)

        # Source writes to the allocated bus
        server.synth("perc_src", target=int(src_group),
                     out_bus=float(int(fx_bus)), frequency=440.0)
        time.sleep(2.0)
    # bus freed automatically

Supernova (Parallel DSP)

Use supernova instead of scsynth to distribute independent synth nodes across CPU cores. The API is identical -- just pass a different protocol:

import time
from nanosynth import AddAction, EmbeddedSupernovaProtocol, Options, Server
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.synthdef import DoneAction, SynthDefBuilder
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(frequency=440.0, amplitude=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["frequency"]) * builder["amplitude"]
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.5, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    Out.ar(bus=0, source=Pan2.ar(source=sig * env))

voice = builder.build(name="voice")

# Boot supernova instead of scsynth
with Server(
    Options(verbosity=0, load_synthdefs=False),
    protocol=EmbeddedSupernovaProtocol(),
) as server:
    voice.send(server)
    time.sleep(0.1)

    # ParGroup: children execute in parallel across CPU cores
    par = server.par_group(target=1, action=AddAction.ADD_TO_HEAD)

    for freq in [261.63, 329.63, 392.00, 523.25]:
        server.synth("voice", target=par, frequency=freq, amplitude=0.15)

    time.sleep(3.0)

With scsynth, all voices execute sequentially on one audio thread. With supernova, voices inside a ParGroup are distributed across cores -- providing a measurable speedup for dense polyphony and independent effect chains.

Recording

Capture real-time audio output to a file:

import time
from nanosynth import Server

with Server() as server:
    synthdef.send(server)
    time.sleep(0.1)

    # Start recording to WAV
    server.record("output.wav", header_format="wav", sample_format="int16")

    # Play some audio
    node = server.synth("sine", frequency=440.0, amplitude=0.3)
    time.sleep(2.0)
    server.free(node)

    # Stop recording -- finalizes the file
    server.stop_recording()

Recording options include num_channels (defaults to output bus count), bus (which bus to record from), header_format ("wav" or "aiff"), and sample_format ("int16", "int24", "float").

Buffer Data Exchange (numpy)

Because the engine runs in-process, you can move sample data between a numpy array and a buffer's memory with a direct memcpy -- no OSC /b_setn//b_getn round-trip and none of their datagram-size limits. This is the concrete advantage of the embedded engine over an out-of-process SuperCollider client: cheap, arbitrary-size, two-way transfer with the numpy/scipy/ML stack.

Install the optional dependency with pip install nanosynth[numpy].

import numpy as np
from nanosynth import Server

with Server() as server:
    # Load a numpy array straight into a new buffer (allocates + fills).
    wavetable = np.sin(np.linspace(0, 2 * np.pi, 1024, endpoint=False)).astype("float32")
    buffer_id = server.alloc_buffer_from_array(wavetable)

    # Inspect it: (frames, channels, sample_rate)
    frames, channels, sample_rate = server.buffer_info(buffer_id)

    # Read samples back into numpy -- shape (frames, channels), float32.
    data = server.get_buffer_data(buffer_id)

    # Process in numpy and write back in place (shape must match the buffer).
    server.set_buffer_data(buffer_id, np.tanh(data * 2.0))

get_buffer_data always returns a 2-D (frames, channels) array (mono is (frames, 1)); set_buffer_data accepts 1-D (mono) or 2-D input and coerces it to contiguous float32. These methods read and write the live buffer, so for clean results the buffer should not be in active use by a synth during the transfer (a concurrent read/write may tear or glitch -- it never crashes). Direct buffer access is available on the embedded scsynth engine only (not supernova).

Introspection and Control

Query the running engine and inspect or reset its node graph:

from nanosynth import Server

with Server() as server:
    status = server.status()      # CPU load, sample rate, node/synth/ugen counts
    print(status.num_synths, status.actual_sample_rate)

    version = server.version()    # program name and version

    # The live node tree as a nested NodeInfo (groups, synths, control values).
    tree = server.query_tree(controls=True)
    for child in tree.children:
        print(child.node_id, "group" if child.is_group else child.synthdef)

    # "Panic": free all nodes, clear the scheduler, recreate the default group.
    server.reset()

status(), version(), and query_tree() block for a reply and raise EngineError on timeout. reset() frees all nodes and resets node-id allocation but leaves loaded SynthDefs, buffers, and buses intact.

Node Lifecycle Notifications

Register for node events to observe nodes being created, freed, paused, resumed, or moved. This is the only way to know when a synth that frees itself (via a DoneAction envelope) has actually finished:

from nanosynth import Server

with Server() as server:
    synthdef.send(server)
    server.enable_notifications()

    # Observe every node event.
    server.on_node(lambda e: print(e.action, e.node_id))

    # ...or wait for one specific node to free itself.
    node = server.synth("ping", frequency=440.0)  # a self-freeing percussive synth
    if node.wait_free(timeout=3.0):
        print("the synth finished and freed itself")

Each callback receives a NodeEvent with action ("go", "end", "off", "on", "move"), node_id, parent_group_id, is_group, and (for groups) head_node_id/tail_node_id. Synth.on_free(callback) is a one-shot convenience that fires when that specific node ends. Notifications are off by default; enable_notifications() waits for the engine to confirm registration so the first node's /n_go is not missed.

Offline (NRT) Rendering

Render audio to a file without real-time audio hardware -- useful for batch processing, testing, and CI pipelines:

from nanosynth import Score, SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

# Define a SynthDef
with SynthDefBuilder(freq=440.0, amp=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["freq"]) * builder["amp"]
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.01, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    Out.ar(bus=0, source=Pan2.ar(source=sig * env))
sd = builder.build(name="sine")

# Build a Score -- a sequence of timestamped OSC commands
score = Score()
score.add_synthdef(0.0, sd)
score.add_synth(0.0, "sine", freq=440.0, amp=0.3)
score.add_synth(0.5, "sine", freq=554.37, amp=0.2)
score.add_synth(1.0, "sine", freq=659.26, amp=0.2)

# Render to a WAV file (no audio hardware needed)
score.render("output.wav", sample_rate=44100, header_format="WAV", sample_format="int16")

Pattern Sequencing

Replace manual time.sleep() loops with musical patterns. Pbind binds keys to patterns or scalars to produce event streams; Clock drives playback at a given tempo:

import time
from nanosynth import Options, Server
from nanosynth.patterns import Clock, Pbind, Prand, Pseq, Pwhite, Rest

with Server(Options(verbosity=0)) as server:
    # (assume a "default" SynthDef with freq, amp, gate params is loaded)
    clock = Clock(bpm=140)

    # Ascending melody
    melody = Pbind(
        instrument="default",
        freq=Pseq([261.63, 293.66, 329.63, 392.00, 440.00]),
        dur=Pseq([0.5, 0.5, 0.5, 0.5, 1.0]),
        amp=0.2,
    )
    melody.play(clock, server)
    time.sleep(3.0)

    # Randomized melody with rests and dynamic amplitude
    rand_melody = Pbind(
        instrument="default",
        freq=Prand([261.63, 329.63, 392.00, 440.00], repeats=8),
        dur=Pseq([0.25, 0.25, Rest(0.5), 0.5], repeats=2),
        amp=Pwhite(0.1, 0.25, repeats=8),
    )
    player = rand_melody.play(clock, server)
    time.sleep(4.0)

    player.stop()
    clock.stop()

Available patterns: Pseq (sequential), Prand (random choice), Pwhite (uniform random float), Pseries (arithmetic series), Pgeom (geometric series), Pchoose (weighted random), Pn (repeat N times), Pconst (yield until sum reaches total). Patterns support chaining with | and preview with .take(n).

MIDI Input

Connect hardware MIDI controllers. Requires the _midi C extension (built by default with NANOSYNTH_EMBED_MIDI=ON):

import time
from nanosynth import Options, Server
from nanosynth.midi import MidiIn, midi_note_map, midi_cc_map

# List available MIDI ports
print(MidiIn.list_ports())

with Server(Options(verbosity=0)) as server:
    # (assume a gated SynthDef "synth" is loaded)

    with MidiIn(port=0) as midi:
        # Polyphonic note mapping: note-on creates synth, note-off sends gate=0
        cleanup_notes = midi_note_map(midi, server, "synth")

        # Map CC1 (mod wheel) to a parameter on an existing synth
        # cleanup_cc = midi_cc_map(midi, server, some_synth,
        #                          cc_map={1: "cutoff"}, range_min=200.0, range_max=8000.0)

        # Or register handlers directly
        midi.on_note_on(lambda msg: print(f"Note {msg.note} vel {msg.velocity}"))
        midi.on_cc(lambda msg: print(f"CC {msg.control} = {msg.value}"))

        input("Press Enter to quit...")
        cleanup_notes()

NodeProxy / Ndef (Live Coding)

Hot-swap synth definitions while audio plays. NodeProxy manages a private audio bus, source synth, and monitor synth -- swapping replaces only the source with a crossfade:

import time
from nanosynth import Options, Server
from nanosynth.proxy import Ndef, NodeProxy
from nanosynth.ugens import LFNoise1, LPF, Saw, SinOsc

with Server(Options(verbosity=0)) as server:
    # NodeProxy: manual usage
    proxy = NodeProxy(server)
    proxy.source = lambda: SinOsc.ar(frequency=440) * 0.2
    proxy.play()
    time.sleep(2.0)

    # Hot-swap to saw wave (crossfades automatically)
    proxy.source = lambda: Saw.ar(frequency=330) * 0.15
    time.sleep(2.0)

    proxy.clear()

    # Ndef: concise named proxy registry
    Ndef(server, "pad", lambda: SinOsc.ar(frequency=220) * 0.2)
    Ndef(server, "pad").play()
    time.sleep(1.5)

    # Hot-swap via Ndef
    Ndef(server, "pad", lambda: Saw.ar(frequency=165) * 0.15)
    time.sleep(1.5)

    Ndef.clear_all(server)

Synthesis Techniques

The following examples show SynthDef definitions for various synthesis techniques. Each can be played using the Server class as shown above.

Using the @synthdef Decorator

For simpler definitions, use the decorator to skip the builder boilerplate. Parameter rates and lags are specified positionally:

from nanosynth import synthdef, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

@synthdef("kr", ("kr", 0.5))  # freq: control rate, amp: control rate with 0.5s lag
def my_sine(freq=440.0, amp=0.3):
    sig = SinOsc.ar(frequency=freq)
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.01, release_time=1.0),
        done_action=DoneAction.FREE_SYNTH,
    )
    Out.ar(bus=0, source=Pan2.ar(source=sig * amp * env))

scgf_bytes = my_sine.compile()  # my_sine is a SynthDef instance

Subtractive Synthesis

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import LFNoise1, LPF, Out, Pan2, RLPF, Saw, WhiteNoise, XLine

# Saw wave through a sweeping low-pass filter
with SynthDefBuilder(frequency=110.0, amplitude=0.4) as builder:
    sig = Saw.ar(frequency=builder["frequency"])
    cutoff = XLine.kr(start=8000.0, stop=200.0, duration=3.0,
                      done_action=DoneAction.FREE_SYNTH)
    sig = LPF.ar(source=sig, frequency=cutoff)
    sig = sig * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

filtered_saw = builder.build(name="filtered_saw")

# White noise through a resonant LPF with LFO-modulated cutoff
with SynthDefBuilder(amplitude=0.15) as builder:
    sig = WhiteNoise.ar()
    lfo = LFNoise1.kr(frequency=4.0)
    cutoff = lfo * 1900.0 + 2100.0  # map [-1,1] to [200, 4000]
    sig = RLPF.ar(source=sig, frequency=cutoff, reciprocal_of_q=0.1)
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.5, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

resonant_noise = builder.build(name="resonant_noise")

FM Synthesis

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(
    carrier_freq=440.0, mod_ratio=2.0, mod_index=3.0,
    amplitude=0.3, gate=1.0,
) as builder:
    mod_freq = builder["carrier_freq"] * builder["mod_ratio"]
    modulator = SinOsc.ar(frequency=mod_freq) * builder["mod_index"] * mod_freq
    carrier = SinOsc.ar(frequency=builder["carrier_freq"] + modulator)
    env = EnvGen.kr(
        envelope=Envelope.adsr(
            attack_time=0.01, decay_time=0.1, sustain=0.7, release_time=0.3,
        ),
        gate=builder["gate"],
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = carrier * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

fm_synth = builder.build(name="fm_synth")

Additive Synthesis

Sum harmonics with decreasing amplitude to build a rich tone from pure sine partials:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(frequency=200.0, amplitude=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["frequency"]) * 1.0
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 2.0) * 0.5
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 3.0) * 0.33
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 4.0) * 0.25
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 5.0) * 0.2
    sig = sig * 0.3  # normalize
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.01, release_time=2.0),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

additive = builder.build(name="additive")

Plucked String (Physical Modeling)

Karplus-Strong style plucked string using the Pluck UGen:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Dust, Out, Pan2, Pluck, WhiteNoise

with SynthDefBuilder(frequency=440.0, amplitude=0.5, decay=5.0) as builder:
    trig = Dust.ar(density=1.0)
    sig = Pluck.ar(
        source=WhiteNoise.ar(),
        trigger=trig,
        maximum_delay_time=1.0 / 100.0,
        delay_time=1.0 / builder["frequency"],
        decay_time=builder["decay"],
        coefficient=0.3,
    )
    sig = sig * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

pluck = builder.build(name="plucked_string")

Delay and Reverb Effects

Process a dry signal through comb delay and FreeVerb:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import CombC, FreeVerb, Out, Pan2, Saw, LPF

with SynthDefBuilder(frequency=220.0, amplitude=0.3) as builder:
    # Dry signal: filtered saw
    dry = Saw.ar(frequency=builder["frequency"])
    dry = LPF.ar(source=dry, frequency=2000.0)
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.005, release_time=0.3),
        done_action=DoneAction.FREE_SYNTH,
    )
    dry = dry * env * builder["amplitude"]

    # Comb delay for metallic echo
    sig = CombC.ar(
        source=dry,
        maximum_delay_time=0.2,
        delay_time=0.15,
        decay_time=2.0,
    )

    # Reverb
    sig = FreeVerb.ar(source=dry + sig, mix=0.4, room_size=0.8, damping=0.3)
    Out.ar(bus=0, source=Pan2.ar(source=sig))

delay_reverb = builder.build(name="delay_reverb")

Demand-Rate Sequencing

Use demand UGens to sequence pitches without host-side scheduling:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Duty, Dseq, Out, Pan2, SinOsc

with SynthDefBuilder(amplitude=0.3) as builder:
    # Dseq loops a sequence of MIDI-note frequencies at demand rate
    freq_pattern = Dseq.dr(
        repeats=4,
        sequence=[261.63, 293.66, 329.63, 392.00, 440.00, 392.00, 329.63, 293.66],
    )
    # Duty reads from the demand pattern every 0.25 seconds
    freq = Duty.kr(duration=0.25, level=freq_pattern)
    sig = SinOsc.ar(frequency=freq) * builder["amplitude"]
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.01, sustain_time=7.9, release_time=0.1),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env
    Out.ar(bus=0, source=Pan2.ar(source=sig))

sequencer = builder.build(name="sequencer")

Ring Modulation

Multiply two signals together for classic ring modulation:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import LFTri, Out, Pan2, SinOsc

with SynthDefBuilder(
    carrier_freq=440.0, mod_freq=60.0, amplitude=0.3,
) as builder:
    carrier = SinOsc.ar(frequency=builder["carrier_freq"])
    modulator = LFTri.ar(frequency=builder["mod_freq"])
    sig = carrier * modulator  # ring mod = simple multiplication
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.05, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

ring_mod = builder.build(name="ring_mod")

Stereo Width with Detuning

Fatten a sound by panning two slightly detuned oscillators:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import LPF, Out, Saw

with SynthDefBuilder(frequency=110.0, detune=0.5, amplitude=0.4) as builder:
    left = Saw.ar(frequency=builder["frequency"] - builder["detune"])
    right = Saw.ar(frequency=builder["frequency"] + builder["detune"])
    left = LPF.ar(source=left, frequency=3000.0)
    right = LPF.ar(source=right, frequency=3000.0)
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.1, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    left = left * env * builder["amplitude"]
    right = right * env * builder["amplitude"]
    Out.ar(bus=0, source=[left, right])  # direct stereo output

stereo_saw = builder.build(name="stereo_saw")

Dynamics Processing

Apply compression to a signal using Compander:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Compander, Dust, Out, Pan2, Ringz

with SynthDefBuilder(amplitude=0.5) as builder:
    # Sparse impulses through a resonant filter -- wide dynamic range
    sig = Ringz.ar(
        source=Dust.ar(density=3.0),
        frequency=2000.0,
        decay_time=0.2,
    )
    # Compress: bring quiet parts up, loud parts down
    sig = Compander.ar(
        source=sig,
        control=sig,
        threshold=0.3,
        slope_below=2.0,   # expand below threshold
        slope_above=0.5,   # compress above threshold
        clamp_time=0.01,
        relax_time=0.1,
    )
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.01, sustain_time=3.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

compressed = builder.build(name="compressed")

Advanced Features

SynthDef Compilation (No Engine Required)

SynthDef graphs can be compiled to SuperCollider's SCgf binary format without booting the audio engine -- useful for generating SynthDefs for any SuperCollider server:

from nanosynth import SynthDefBuilder, compile_synthdefs
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(frequency=440.0) as builder:
    Out.ar(bus=0, source=Pan2.ar(source=SinOsc.ar(frequency=builder["frequency"])))

synthdef = builder.build(name="sine")
scgf_bytes = synthdef.compile()

# Or compile multiple SynthDefs into a single SCgf blob
blob = compile_synthdefs(synthdef1, synthdef2, synthdef3)

Debugging SynthDef Graphs

SynthDef.dump_ugens() prints a human-readable UGen graph (like SuperCollider's SynthDef.dumpUGens):

print(synthdef.dump_ugens())
# SynthDef: sine
#   0: Control.kr - frequency, amplitude
#   1: SinOsc.ar(frequency: Control[0], phase: 0.0)
#   2: BinaryOpUGen.ar(MULTIPLICATION, a: SinOsc[0], b: Control[1])
#   ...

SynthDef Graph Introspection

Walk the UGen graph programmatically or export to Graphviz DOT format:

# Structured graph -- returns UGenNode/UGenInput NamedTuples
graph = sd.graph()
for node in graph.nodes:
    print(f"{node.node_index}: {node.type_name}.{node.rate}")
    for inp in node.inputs:
        if inp.source is not None:
            print(f"  {inp.name} <- {inp.source.type_name}[{inp.output_index}]")
        else:
            print(f"  {inp.name} = {inp.value}")

# Export to Graphviz DOT
dot = sd.to_dot(rankdir="LR")
print(dot)  # pipe to `dot -Tpng -o graph.png`

OSC Codec

The OSC module works standalone for any OSC communication needs:

from nanosynth import OscMessage, OscBundle

# Encode
msg = OscMessage("/s_new", "sine", 1000, 0, 1, "frequency", 440.0)
datagram = msg.to_datagram()

# Decode
decoded = OscMessage.from_datagram(datagram)
assert decoded == msg

# Bundles
bundle = OscBundle(
    timestamp=None,  # immediately
    contents=[
        OscMessage("/s_new", "sine", 1000, 0, 1),
        OscMessage("/n_set", 1000, "frequency", 880.0),
    ],
)
bundle_bytes = bundle.to_datagram()

Available UGens

Organized by category:

Category UGens
Oscillators SinOsc, Saw, Pulse, Blip, Klank, LFSaw, LFPulse, LFTri, LFCub, LFPar, VarSaw, SyncSaw, Impulse, FSinOsc, LFGauss, Vibrato, Osc, OscN, COsc, VOsc, VOsc3
Filters LPF, HPF, BPF, BRF, RLPF, RHPF, MoogFF, Lag, Lag2, Lag3, LagUD, Lag2UD, Lag3UD, Ramp, Decay, Decay2, Ringz, Formlet, Median, LeakDC, OnePole, OneZero, TwoPole, TwoZero, APF, FOS, SOS, MidEQ, Slew, Slope, Integrator, DetectSilence, Changed
BEQ Filters BLowPass, BHiPass, BBandPass, BBandStop, BAllPass, BLowShelf, BHiShelf, BPeakEQ, BLowCut, BHiCut
Noise WhiteNoise, PinkNoise, BrownNoise, GrayNoise, ClipNoise, Dust, Dust2, Crackle, LFNoise0, LFNoise1, LFNoise2, LFDNoise0, LFDNoise1, LFDNoise3, LFClipNoise, LFDClipNoise, Logistic
Stochastic Gendy1, Gendy2, Gendy3
Delays DelayN, DelayL, DelayC, Delay1, Delay2, CombN, CombL, CombC, AllpassN, AllpassL, AllpassC, BufDelayN, BufDelayL, BufDelayC, BufCombN, BufCombL, BufCombC, BufAllpassN, BufAllpassL, BufAllpassC, DelTapRd, DelTapWr
Envelopes EnvGen, Linen, Done, Free, FreeSelf, FreeSelfWhenDone, Pause, PauseSelf, PauseSelfWhenDone
Panning Pan2, Pan4, PanAz, PanB, PanB2, BiPanB2, Balance2, Rotate2, DecodeB2, XFade2, Splay
Demand Dseq, Dser, Dseries, Drand, Dxrand, Dshuf, Dwrand, Dwhite, Dbrown, Diwhite, Dibrown, Dgeom, Demand, Duty, DemandEnvGen, Dbufrd, Dbufwr, Dstutter, Dreset, Dswitch, Dswitch1, Dunique
Dynamics Compander, CompanderD, Limiter, Normalizer, Amplitude
Chaos LorenzL, HenonN/L/C, GbmanN/L, LatoocarfianN/L/C, LinCongN/L/C, CuspN/L, QuadN/L/C, StandardN/L, FBSineN/L/C
Granular GrainBuf, GrainIn, PitchShift, Warp1
Buffer I/O PlayBuf, RecordBuf, BufRd, BufWr, ClearBuf, LocalBuf, MaxLocalBufs, ScopeOut, ScopeOut2
Disk I/O DiskIn, DiskOut, VDiskIn
Physical Modeling Pluck, Ball, TBall, Spring
Reverb FreeVerb
Convolution Convolution, Convolution2, Convolution2L, Convolution3
Phase Vocoder FFT, IFFT, PV_Add, PV_BinScramble, PV_BinShift, PV_BinWipe, PV_BrickWall, PV_ConformalMap, PV_Conj, PV_Copy, PV_CopyPhase, PV_Diffuser, PV_Div, PV_HainsworthFoote, PV_JensenAndersen, PV_LocalMax, PV_MagAbove, PV_MagBelow, PV_MagClip, PV_MagDiv, PV_MagFreeze, PV_MagMul, PV_MagNoise, PV_MagShift, PV_MagSmear, PV_MagSquared, PV_Max, PV_Min, PV_Mul, PV_PhaseShift, PV_PhaseShift90, PV_PhaseShift270, PV_RandComb, PV_RandWipe, PV_RectComb, PV_RectComb2, RunningSum
Machine Listening BeatTrack, BeatTrack2, KeyTrack, Loudness, MFCC, Onsets, Pitch, SpecCentroid, SpecFlatness, SpecPcile
Hilbert FreqShift, Hilbert, HilbertFIR
I/O In, Out, InFeedback, LocalIn, LocalOut, OffsetOut, ReplaceOut, XOut
Lines Line, XLine, LinExp, LinLin, DC, K2A, A2K, AmpComp, AmpCompA, Silence
Triggers Trig, Trig1, Latch, Gate, Schmidt, Sweep, Phasor, Peak, PeakFollower, RunningMax, RunningMin, SendTrig, Poll, SendReply, SendPeakRMS, ToggleFF, TDelay, ZeroCrossing, LeastChange, MostChange, Clip, Fold, Wrap, InRange
Mouse/Keyboard KeyState, MouseButton, MouseX, MouseY
Info SampleRate, SampleDur, BlockSize, ControlRate, ControlDur, SubsampleOffset, RadiansPerSample, NumRunningSynths, BufFrames, BufSamples, BufSampleRate, BufRateScale, BufChannels, BufDur, NumOutputBuses, NumInputBuses, NumAudioBuses, NumControlBuses, NumBuffers, NodeID
Random Rand, IRand, ExpRand, LinRand, NRand, TRand, TIRand, TExpRand, CoinGate, TWindex, RandID, RandSeed, Hasher, MantissaMask
Utility MulAdd, Sum3, Sum4, Mix
Safety CheckBadValues, Sanitize

Envelope Types

from nanosynth import Envelope

Envelope.adsr(attack_time=0.01, decay_time=0.3, sustain=0.5, release_time=1.0)
Envelope.asr(attack_time=0.01, sustain=1.0, release_time=1.0)
Envelope.linen(attack_time=0.01, sustain_time=1.0, release_time=1.0)
Envelope.percussive(attack_time=0.01, release_time=1.0)
Envelope.triangle(duration=1.0, amplitude=1.0)

# Custom envelope
Envelope(amplitudes=[0, 1, 0.5, 0], durations=[0.1, 0.3, 0.6], curves=[-4])

Documentation

API reference docs are auto-generated from docstrings using mkdocs-material and mkdocstrings.

make docs        # build static site to site/
make docs-serve  # serve locally at http://127.0.0.1:8000 with live reload
make docs-deploy # deploy to GitHub Pages

Browse the docs at shakfu.github.io/nanosynth.

Development

make dev              # uv sync + editable install
make build            # build wheel (incremental via build cache)
make sdist            # build source distribution
make test             # run tests
make lint             # ruff check --fix
make format           # ruff format
make typecheck        # mypy --strict
make qa               # all of the above
make demos            # run scsynth demo scripts
make demos-supernova  # run supernova demo scripts
make clean            # remove transitory files (preserves build cache)
make reset            # clean everything including build cache

CI

The GitHub Actions workflow (.github/workflows/build.yml) builds wheels for CPython 3.10--3.14 on macOS ARM64, Linux x86_64, and Windows x86_64 using cibuildwheel. A qa job runs lint, format check, typecheck, and tests on every push. A source distribution ('sdist') is built separately and all artifacts are aggregated into a single downloadable archive.

A separate release workflow (.github/workflows/release.yml) publishes to PyPI on tag push via trusted publisher, with manual dispatch for TestPyPI.

Related Projects

  • synthdef -- a native Rust port of nanosynth. It reimplements the SynthDef compiler (validated byte-for-byte against nanosynth's output via shared golden fixtures and the same language-neutral UGen spec, spec/nanosynth-ugens.json), and builds a full stack on top: a nanosynth-style Server and SuperCollider-style pattern sequencing over the pure-Rust plyphon engine. The goal is a portable core that runs where a Python runtime cannot -- other languages via a C ABI, the browser via WASM, or a DAW plugin.

Attributions

  • SuperCollider -- the audio synthesis engine and programming language that nanosynth embeds.
  • supriya -- the inspiration for nanosynth; its UGen system and SynthDef compiler were the basis for this project's graph compilation pipeline.
  • sc3 - Another SuperCollider library for Python with less features than supriya.
  • TidalCycles -- live coding pattern language for music, built on SuperCollider.
  • Strudel -- JavaScript port of TidalCycles for browser-based live coding.
  • Sonic Pi -- live coding music synth built on SuperCollider.
  • RtMidi -- cross-platform MIDI I/O library, vendored for the _midi extension.
  • nanobind -- the C++/Python binding library used to embed libscsynth, RtiMidi and the OSC codec.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nanosynth-0.3.0.tar.gz (7.9 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

nanosynth-0.3.0-cp314-cp314-win_amd64.whl (2.7 MB view details)

Uploaded CPython 3.14Windows x86-64

nanosynth-0.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (4.0 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.3.0-cp314-cp314-macosx_11_0_arm64.whl (2.4 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

nanosynth-0.3.0-cp313-cp313-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.13Windows x86-64

nanosynth-0.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (4.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.3.0-cp313-cp313-macosx_11_0_arm64.whl (2.4 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

nanosynth-0.3.0-cp312-cp312-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.12Windows x86-64

nanosynth-0.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (4.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.3.0-cp312-cp312-macosx_11_0_arm64.whl (2.4 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

nanosynth-0.3.0-cp311-cp311-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.11Windows x86-64

nanosynth-0.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (4.0 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.3.0-cp311-cp311-macosx_11_0_arm64.whl (2.4 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

nanosynth-0.3.0-cp310-cp310-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.10Windows x86-64

nanosynth-0.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (4.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.3.0-cp310-cp310-macosx_11_0_arm64.whl (2.4 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file nanosynth-0.3.0.tar.gz.

File metadata

  • Download URL: nanosynth-0.3.0.tar.gz
  • Upload date:
  • Size: 7.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.3.0.tar.gz
Algorithm Hash digest
SHA256 6636f856984deccc43d824b1d9a51c753ab82677d2f2b1a6ea3fbf57bc412c16
MD5 da1f06e9e3287ec3d1499fb8a0341faf
BLAKE2b-256 1a147014220176aa17b95b1a82d0d42e8b05dddc772d4f364f81673049c626da

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.3.0-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: CPython 3.14, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.3.0-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 c49078919267c32881b3b5bd3a232116fa38a069df879711b5eea34dab2d01c1
MD5 dec0efef005ed103c5fe0c338e953b30
BLAKE2b-256 5b76b94e41c60d99c5933f8c9b415c42ae513816d54d6cca28045dc5645d43fa

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 dd0555baa7b77fa8a2c3f211db085893eaa54186dfe341db71ef29223bc007b1
MD5 0186e6108c00fcb400ee132fe1a3b137
BLAKE2b-256 d4f7abcc8a4c03f31937d7e42f6c14a9d96821cdd2fbe415e9ee773450340bb0

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c6c62dd5e5837a92c4e1bf83214329eaf795596c9e11c42355cd369dfe467b19
MD5 89c25ed8782679adec5ee034807da5d1
BLAKE2b-256 9e3c9e7740ad5013ba4b34c5c6f5cb7f33695d437a1ae28b05a90ba43406583f

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.3.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.3.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 85b6b60ba2e0954ed112e173ecfb6b9f47a8f2cd68d767688cafa52374a8503d
MD5 8b48cc777380a2e5ba2b52aaf7cb7b84
BLAKE2b-256 eefa121eedbd35dabf457fd42640184f0d8f14f0d54c9ac41190351b4376d1ca

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 a3dbae951007e08aab63412d2236e8251db696205b4cee9414fdde71442ac09a
MD5 520f71fc7342d1a376c010409922866c
BLAKE2b-256 14ea3de9072cbc1b4df303ffae8013d01b0ae3df33c7bf1a965bacfdfaccf19f

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7c6d036343e256d2488aa35c58cd6c759b3fa04c393f2aa840739bf9be11f7e7
MD5 7ec6f14e99405b573e8aca2f6a921547
BLAKE2b-256 f9918f7d7629a21eb3f91b4b7290912ec2e5596a7e84cd1eebf996b5a954b65a

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.3.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.3.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 4591f5f84ad3024f02d667e4c01eb8cf65cd7e6a0b1184da879e348445a490c8
MD5 845434cad429fc9a3ccccbe776c9b406
BLAKE2b-256 64ef57871cb800525202589f16ac015856586fb63b8761b77f5fa98d1a3eb536

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 63ca32692670989ccc06385dfe98638fa0170749e9964cf5ca946ac1ec5f039e
MD5 b41e60118a462ff4f90eca47507f9d94
BLAKE2b-256 d65ed6c556c83e9f91350b890bc0f4e959b6f4aff0728aeed3377b2408bd8d51

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 63e9a3f319781c79c3895425b3a9b41eb6e9c743b9994feaca399ecb77571a42
MD5 ffaab0d2684ddb27bb01ff027d859be4
BLAKE2b-256 f451947277fbb0758e1b74ac7d6df4e0a2156afd5d9837026cb6483d6faeb7ca

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.3.0-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.3.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 11edeb983b008fa17d2686c993161f3909341a01ae8b5f38218f05db9127ef54
MD5 7b0876760fa744796b7633ddbe311567
BLAKE2b-256 eed12817f7d890b07f6dbddfb8aefdeb9febdb3ab6730c23c6e740b57abc4433

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 6efb0fc6704a49c812db429af248d5d6891395b525ac3e46e3ca166d5ee7659f
MD5 0f0a0b256962e301b2e29cef114f1778
BLAKE2b-256 8d783da8d11bdc4a90ca42c8255d0a9c827b0e9e8f154917435417e429bd5857

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a5374a10ca8d5af328c9f14ec6afa7636779531645a8200f8983846a9d2a1b13
MD5 a24c4d01a4f8ae14b6cdddc62ba2cacb
BLAKE2b-256 4c6462f994814e9cccee0f6bc512254b5ea64b5f4863ed15d30acc25024e31e6

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.3.0-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.3.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 86ab1d2d1d1309a0c19652eb58714c5b1a3e93f67229f1394624673c7c58ecc1
MD5 a56c8332759665ba9b7946aa58fc8551
BLAKE2b-256 6d61d80c379b2004816aa4b795c8630eb52de1647d73a8e0c6e06ef0a7f5e88c

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 638028c986eddf1142754cfa92a7c8fac71e25617e90e763dde6feff16d0b094
MD5 26db6ea946bf367385275e95fccdca81
BLAKE2b-256 eb35e2619d03a8697565afc39810797f6359d9b636125749ddee646b9c98c2b1

See more details on using hashes here.

File details

Details for the file nanosynth-0.3.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.3.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b6bf1b94a43d10f01d968840d155812f7a9709075e372f4517836e8884da73e1
MD5 fa3a67f5edc9831dae8836eeadc5c0c9
BLAKE2b-256 c8e717bd35db76edf60fe51fef57b7efa89d7384e4ef827e52fc7cae2d7b67ba

See more details on using hashes here.

Release history Release notifications | RSS feed

0.3.1

16 files

This release

0.3.0 This release

16 files

0.2.1

16 files

0.2.0

16 files

0.1.6

16 files

0.1.5

16 files

0.1.4

16 files

0.1.3

16 files

0.1.2

16 files

0.1.1

16 files

0.1.0

14 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page