Street Legends is our fast five-a-side street football game for iPhone, with a league to climb, a nine-player squad and kits to unlock. Open the app bundle and you will not find a single PNG or audio file. Every visual and every sound is generated in Swift at runtime.
Why build a game this way?
- Tiny download: no texture atlases or audio packs, so the app installs in seconds
- Sharp at every size: shapes are drawn for the exact screen, from the smallest iPhone to an iPad
- Infinite variations: a new kit is a few colours, not a new set of images
- Fast iteration: change a number and the whole game updates, no export step
The trade-off is that you cannot hand a file to an illustrator. It suits stylized, geometric art, which is exactly the look we wanted.
Drawing sprites with Core Graphics
Drawing complex shapes with SKShapeNode every frame is expensive. Instead, we draw once into an image with UIGraphicsImageRenderer and turn it into a texture:
import SpriteKit
import UIKit
func ballTexture(diameter: CGFloat) -> SKTexture {
let size = CGSize(width: diameter, height: diameter)
let image = UIGraphicsImageRenderer(size: size).image { context in
let rect = CGRect(origin: .zero, size: size)
UIColor.white.setFill()
context.cgContext.fillEllipse(in: rect)
UIColor.black.setFill()
let patch = rect.insetBy(dx: diameter * 0.34, dy: diameter * 0.34)
context.cgContext.fillEllipse(in: patch)
}
return SKTexture(image: image)
}
let ball = SKSpriteNode(texture: ballTexture(diameter: 28))
Textures are cached by their parameters, so each one is drawn once and reused by every node that needs it. Kits work the same way: a jersey shape filled with the team's colours and a number drawn on top.
Synthesizing sound with AVAudioEngine
Sound effects are short waveforms with an envelope. AVAudioSourceNode lets you write samples directly:
import AVFoundation
final class Synth {
private let engine = AVAudioEngine()
private var phase = 0.0
private var frequency = 0.0
private var amplitude = 0.0
init() {
let sampleRate = engine.outputNode.inputFormat(forBus: 0).sampleRate
let source = AVAudioSourceNode { [unowned self] _, _, frameCount, bufferList -> OSStatus in
let buffers = UnsafeMutableAudioBufferListPointer(bufferList)
for frame in 0..<Int(frameCount) {
let sample = Float(sin(phase) * amplitude)
phase += 2 * .pi * frequency / sampleRate
if phase > 2 * .pi { phase -= 2 * .pi }
amplitude *= 0.9993 // quick decay, like a plucked sound
for buffer in buffers {
buffer.mData?.assumingMemoryBound(to: Float.self)[frame] = sample
}
}
return noErr
}
let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: 1)
engine.attach(source)
engine.connect(source, to: engine.mainMixerNode, format: format)
try? engine.start()
}
func kick() {
frequency = 90
amplitude = 0.7
}
}
A kick is a low tone that drops quickly. A whistle is a high tone with a little vibrato. Crowd noise is filtered random noise that swells after a goal. Combining a handful of these generators covers every sound in the game.
Keeping it fast
- Draw textures once, at load time, never inside
update(_:) - Reuse nodes for repeated effects instead of creating new ones
- Use SpriteKit physics bodies with simple shapes, circles for players and the ball
- Profile on the oldest device you support, not on the newest
When procedural art is the right call
It shines for arcade games, puzzle games and stylized worlds where geometry is the aesthetic. For painterly or character-heavy art, an artist and a texture atlas are still the better tool; our match-3 game Paw & Home is a good example of that side.
Want a game built? Tell us about it. We build in SpriteKit, SceneKit and Unity.
Frequently asked questions
Is SpriteKit still a good choice for iOS games in 2026?
Yes, for 2D games on Apple platforms. It is light, native and well integrated with Game Center and StoreKit. For cross-platform or 3D-heavy games, Unity is usually the better fit.
Does procedural audio sound worse than recorded audio?
For short effects like kicks, clicks and whistles it can sound great and stays tiny. For music and voices, recordings are still the better choice.
Does generating textures slow down app launch?
Only if you draw them every frame. Drawn once at launch and cached, simple textures take milliseconds.
How small can a SpriteKit game be?
Without image and audio assets, a complete game can be just a few megabytes, most of it compiled code.