This article is part 1 of a 5 part series introducing the core concepts of Intonal. It is written for all audiences new to Intonal. Follow along with the Intro Part 1 example @ https://intonal.io/

Intonal seeks to find the sweet spot between performance and expressiveness. Each design decision is made to reduce the cognitive load when dealing with audio, though the language is general-purpose to allow creating larger systems. The result enables you to focus on what matters instead of the arcane boilerplate or hard lines between control and audio signals.

For the engineers out there, Intonal is built with the LLVM toolchain, the same compiler technology powering Apple's Swift, Google's Go, Rust, and the PS4/5 toolchain, among other things. That means it runs on the metal and it's easy to support new platforms.

We currently have a public version online that compiles to WebAssembly on the server and runs entirely inside your browser, check it out here to write your own Intonal code: https://intonal.io/

What is dataflow?

Streams at first glance look like variables in any programming language. However, they represent not just a single value but rather a value that changes over time.

Here are some common streams of numbers that you'll see in Intonal.

A constant stream
An impulse, very important in signal processing
A counter, incrementing through all real positive integers
A looper, useful for indexing into arrays, counting beats and many other tasks

Working with sound is fundamentally about controlling how values change over time. Streams that repeat at a high frequency (less than 20 times a second, aka 20hz) are perceived as pitch.

A fundamental tool in our sound design toolbox to build repeating streams is the humble phasor.

phasor = 0 fby (prev + (hz/sr)) % 1

Transforming the phasor into more complex streams is the basis of many synthesis techniques.

Phasor with simple transforms to create common waves
Phasor as an index into an existing wavetable to create arbitrary waveforms efficiently

Transforms take in zero or more streams and produce an output stream. Transforms with zero inputs are typically called generators. We can define the phasor transform like this:

phasor = {hz, sr in
  0 fby (prev + (hz/sr)) % 1
}

Just like streams look like variable definitions, applying transforms looks like calling function or applying operators in C or other languages.

p = phasor(440, 44100)

For example, the following Intonal code:

exp = 1 fby prev * 10
counter = 1 fby prev + 1
out = exp * counter

Can be represented visually as:

Let's put this all together to make something that makes sound - a sine wave at 440 hz.

Sound output is handled by a "main" transform that takes the sample rate as a 32-bit floating point value.

main = {sr: float32 in
    hz = 440
    p = phasor(hz, sr)
    out = sin(p * 2 * PI) * 0.5
}

phasor = {hz: float32, sr: float32 in
  out = 0 fby ((prev + (hz/sr)) % 1)
}

PI = 3.14159265358

Here's how it looks:

At this point, you should have a good idea about what makes Intonal different. There is a lot more to see - a robust type system, rich control structures, container types and more. We'll be putting out a series of blog posts, some geared towards beginners and others diving deeper into the language itself.

Try out the web audio runner here or hop onto our Discord channel to say hi to the team!