Creative Coding: Randomness and Noise

Creative Coding: Randomness and Noise

When discussing coding, the first thing it is usually associated with is the process of sitting down, thinking about algorithms, and writing the code that executes them, with one main goal in mind: solving problems. It can be a repetitive task we want to automate or a real-world problem spotted in our everyday life (for which we develop an app to make that specific task easier and our lives easier). When creating those algorithms, what we do is essentially transforming an input into a desired output.

However, there is another perspective from which we can approach code, and it is the creative one. For a long time now, people have been using coding as a tool for creative expression, crafting algorithms with an artistic intent in a process of search. Tweaking them little by little, looking for an outcome that is often unpredictable, yet profoundly engaging.

We have already introduced the topic of creative coding and explored many aspects of the Apple developer ecosystem, positioning Swift and SwiftUI as promising candidates for creative exploration.

We have explored the history of creative coding and how to get started with creative coding using Swift and SwiftUI.

Getting started with creative coding using Swift and SwiftUI
Take a first step in the world of creative coding and how to use Swift while working on the craft.

Also, we discovered how to layer different techniques to actually begin your creative journey.

Exploring creative coding with Swift and SwiftUI
Get inspired on how SwiftUI can bridge code and artistry through layered computational art techniques.

In this article we are going much more in deep in the exploration and see how to apply randomness and noise to create uncontrolled behaviors, in the pursuit of what we call the unexpected beauty.

At the core of the creation of computational aesthetics there is the process of layering different techniques, approaches, and procedures - not simply applying single algorithms in isolation, but orchestrating multiple procedural systems that interact in complex ways.

In creative coding, beauty often emerges not from control, but from exploration, from the continuous search within systems that evolve and interact beyond our full prediction. As we combine randomness, noise, and structure, we begin to compose dynamic systems where each iteration becomes a new discovery.

This process mirrors the artistic pursuit itself: finding beauty is a constant process of search, guided by curiosity and shaped by the interplay between intention and serendipity. Through Swift and SwiftUI, this search becomes tangible, code turns into a canvas where logic and chance coexist, allowing us to uncover unexpected patterns and aesthetic expressions born from the orchestration of complexity.

Canvas, Path and TimelineView

SwiftUI provides the tools to make the structure for this exploration immediate and intuitive. Within this environment, the Canvas becomes the meeting point between concept and execution, a space where code translates directly into visual form.

It invites iteration and experimentation, encouraging us to think visually through algorithms and to treat each adjustment of parameters, each variation of noise or randomness, as part of an evolving artistic dialogue. This hands-on feedback loop between code and result deepens our understanding of both the medium and the aesthetic systems we create.

Canvas supports immediate mode drawing. It gives us a context to draw with and its size. The context provides an API for drawing, masking, applying filters, and transformations.

Canvas { context, size in
    // Circle Size
    let center = CGPoint(
        x: size.width / 2,
        y: size.height / 2
    )
    
    // Generating the Circle Path
    let path = generateCirclePath(
        x: center.x - radius,
        y: center.y - radius,
        width: radius * 2,
        height:  radius * 2
    )

}

To draw a circle, for example, we need to calculate where we want to draw the circle and create a path to be drawn. Then, the stroke(_:) method from the context type allows to draw the circle.

Canvas { context, size in
    ...
    // Draw the Circle Path
    context.stroke(
        path, with: .color(strokeColor),
        lineWidth: strokeWidth
    )
}

Path comes with methods for defining the outlines of 2D shapes such as:

By playing around with fill color, radius size, stroke width values, you can create variations of the same shape.

Working with points allows us to build custom shapes:

var points: [CGPoint] = []

// 1. Create 12 points around a circle
for i in 0..<12 {
    let angle = (Double(i) / 12.0) * 2 * .pi
    let x = cos(angle) * size
    let y = sin(angle) * size
    points.append(CGPoint(x: x, y: y))
}

// 2. Connect the points
let path = Path { path in

    // 3. Move to the first point 
    path.move(to: points[0])
    for i in 0..<points.count {
        let next = points[(i + 1) % points.count]
        let control = CGPoint(
            x: (points[i].x + next.x) / 2.0,
            y: (points[i].y + next.y) / 2.0
        )
        // 3. Connect the points
        path.addQuadCurve(to: next, control: control)
    }
    // 4. Close the path
    path.closeSubpath()
}

// 5. Position the circle at the center
let transform = CGAffineTransform.identity
    .translatedBy(x: position.x, y: position.y)
    
return path.applying(transform)

By generating points through simple trigonometric relationships, we begin to see how mathematical logic serves as a medium for visual exploration. Each parameter we tweak, such as the number of points, their distance from the center, or the formulas that determine their placement, changes the resulting pattern in ways that can be both subtle and surprising. This process embodies the essence of creative coding: embracing iteration and variation as a means of discovery.

By introducing time as an element in our systems, movement becomes another layer of expression. The TimelineView allows us to connect our drawings to the passing of time, transforming static compositions into evolving ones. Through subtle rotations, oscillations, or shifts in position, we start to perceive rhythm and flow, qualities that emerge not from complex formulas, but from simple transformations applied over time.

This temporal dimension turns our Canvas into a living system, where code continuously generates new states, inviting us to observe and interact with the unfolding dynamics of our creation.

0:00
/0:15

When drawing many shapes in motion and positioning them on the screen, by applying variation to the movement of our shapes, we can begin to see patterns emerge. Exploring those patterns is where our search for beauty truly starts to take form.

Randomness and Noise

Incorporating randomness and noise into the properties of our drawings makes things even more interesting. When we start introducing randomness into our systems, we open the door to a world of unpredictability.

Randomness breaks repetition, injects variation, and allows our creations to behave in ways that feel organic, alive. Yet, in the context of creative coding, rather than chaos, we are more interested in the patterns that emerge from it. To explore these behaviors systematically, we can define structures that help us control how randomness is applied, and that’s where our noise generator comes in.

protocol NoiseGenerator {
    func getValue(x: Double, y: Double, time: Double) -> Double
}

By defining a shared interface for our noise generator, we make experimentation modular and flexible. This abstraction enables us to swap different noise algorithms without rewriting the logic that utilizes them, much like changing a brush in painting, while maintaining the same canvas. It’s a foundational step that gives us creative freedom to focus on how the results look and feel, rather than on the technical details of how each noise function works internally.

Then we can proceed with defining the various types of noise.

Uniform Noise

Uniform noise is a type of noise generator where each value has an equal chance of being generated independently of the input values.

class UniformNoise: NoiseGenerator {
    func getValue(x: Double, y: Double, time: Double) -> Double {
        return Double.random(in: -1...1)
    }
}

It gives us a first glimpse into the raw potential of randomness: every value has an equal chance of appearing, producing visual results that feel energetic and unpredictable. However, such uniform randomness can sometimes seem too harsh or chaotic, primarily when we aim for organic, natural-looking motion.

Non-Uniform Noise

To soften uniform randomness behavior and create more nuanced visual rhythms, we can introduce bias into how our random values are distributed with NonUniformNoise, a type of noise generator where each value has a weighted implementation.

class NonUniformNoise: NoiseGenerator {
    func getValue(x: Double, y: Double, time: Double) -> Double {

        let random1 = Double.random(in: 0...1)
        let random2 = Double.random(in: 0...1)
        
        // Favors smaller values
        // Creates a non-uniform distribution towards 0
        let value = random2 < random1 ? random2 : random1
        
        // Convert from 0...1 to -1...1
        return (value * 2) - 1
    }
}

This approach shifts the balance: instead of relying on pure randomness, we begin to guide the probability of outcomes. It introduces structure within uncertainty, allowing us to simulate behaviors that feel more lifelike and continuous. When applied to visual compositions, these subtle irregularities can add depth and movement, echoing the imperfections found in natural forms.

Applying Noise to the Path

Noise can be applied directly to our geometry to distort the points that define our path, letting it subtly influence each coordinate.

var points: [CGPoint] = []

// 1. Get the source of noise
let noiseGenerator = NonUniformNoise()

// 2. Create 12 points around a circle
for i in 0..<12 {
    let angle = (Double(i) / 12.0) * 2 * .pi
    
    let baseX = cos(angle) * radius
    let baseY = sin(angle) * radius
    
    // 3. Calculate noise
    let noiseX = noiseGenerator.getValue(
        x: Double(i) * 0.1,
        y: time,
        time: time
    )
    // 3. Calculate noise
    let noiseY = noiseGenerator.getValue(
        x: Double(i) * 0.1 + 100,
        y: time,
        time: time
    )
    
    //4. Apply noise to distort the point
    let distortedX = baseX + CGFloat(noiseX) * radius
    let distortedY = baseY + CGFloat(noiseY) * radius
    
    points.append(CGPoint(x: distortedX, y: distortedY))
}

The result is a shape that retains its overall structure but gains organic variation, a blend of order and randomness that reflects the essence of generative design.

0:00
/0:16

This process highlights one of the most engaging aspects of creative coding: the act of tuning and observing. Small adjustments in how we calculate or apply noise can dramatically change the resulting pattern, making every iteration an opportunity for discovery. Yet, while randomness offers infinite variation, it can also make our results unstable or inconsistent, which is why more refined types of noise become essential for deeper exploration.

The thing about “purely random” noise is that it has unpredictable results, which makes it hard to experiment with. Every iteration generates a completely different version of the shape, so ideally, we should work with different types of noise.

Balancing unpredictability with continuity

To gain more artistic control, we look for types of noise that balance unpredictability with continuity ones that evolve smoothly across space and time. This allows our generated visuals to flow naturally, rather than jumping abruptly between states, giving them a sense of coherence that resonates more deeply with human perception.

There are other types of noise algorithms that align well with our experimental goals. They are deterministic, meaning that the same input will always have the same output. They provide a smooth transition between values. They won’t have obvious repetitive patterns.
These structured noise functions act almost like textures for our algorithms, they introduce complexity and variation while maintaining consistency across frames. Each algorithm brings its own aesthetic: some produce smooth, wave-like motions, others yield cellular or granular effects. Choosing between them becomes part of the creative process itself, shaping the tone, rhythm, and emotion of the final composition.

The fun really starts when visualizing multiple elements changing together and playing around with seed values based on the position of the shapes. What happens when the noise is applied to animation speed, distortion, color values, stroke size and/or the size of the shape itself?

Or when you start taking advantage of the hardware capabilities of the device you are creating for?

0:00
/0:23

Using the movement sensors of the iPhone to apply a change to the shapes being drawn. In the same way, you can use the microphone, the camera, and other input methods as a source of noise.

Perlin, Simplex and Worley are examples of noise algorithms that you can implement as noise generators to use as the source of distortion for your shapes. Different algorithms will provide different results with different behaviors. By simply changing how the noise is calculated, we can observe the effect on the behavior of our distortion.

Perlin noise is perhaps the most iconic and widely used in creative coding. Developed by Ken Perlin in the 1980s, it produces smooth, continuous variations that transition gradually between values, creating patterns that feel organic and natural. Unlike purely random noise, Perlin noise generates coherence; nearby points in space or time have similar values, which makes it ideal for simulating natural phenomena like flowing water, drifting clouds, or gently shifting landscapes. In a creative context, it allows us to shape motion and form with a sense of rhythm and harmony, turning raw randomness into something that feels intentional, alive, and expressive.


Creative coding with Swift and SwiftUI is not just about producing visuals; it’s about exploring systems, patterns, and behaviors that emerge from code itself. Through layering, randomness, noise, and motion, we turn logic into a living medium of expression.

Each experiment becomes part of an ongoing dialogue between control and unpredictability, structure and chance. In that space between precision and imperfection, we find what can only be described as unexpected beauty, the beauty of code that surprises even its own creator.