SpriteKit API Reference
Complete API reference for SpriteKit organized by category.
When to Use This Reference
Use this reference when:
-
Looking up specific SpriteKit API signatures or properties
-
Checking which node types are available and their performance characteristics
-
Finding the right physics body creation method
-
Browsing the complete action catalog
-
Configuring SKView, scale modes, or transitions
-
Setting up particle emitter properties
-
Working with SKRenderer or SKShader
Part 1: Node Hierarchy
All Node Types
Node Purpose Batches? Performance Notes
SKNode
Container, grouping N/A Zero rendering cost
SKSpriteNode
Textured sprites Yes (same atlas) Primary gameplay node
SKShapeNode
Vector paths No 1 draw call each — avoid in gameplay
SKLabelNode
Text rendering No 1 draw call each
SKEmitterNode
Particle systems N/A GPU-bound, limit birth rate
SKCameraNode
Viewport control N/A Attach HUD as children
SKEffectNode
Core Image filters No Expensive — cache with shouldRasterize
SKCropNode
Masking No Mask + content = 2+ draw calls
SKTileMapNode
Tile-based maps Yes (same tileset) Efficient for large maps
SKVideoNode
Video playback No Uses AVPlayer
SK3DNode
SceneKit content No Renders SceneKit scene
SKReferenceNode
Reusable .sks files N/A Loads archive at runtime
SKLightNode
Per-pixel lighting N/A Limits: 8 lights per scene
SKFieldNode
Physics fields N/A Gravity, electric, magnetic, etc.
SKAudioNode
Positional audio N/A Uses AVAudioEngine
SKTransformNode
3D rotation wrapper N/A xRotation, yRotation for perspective
SKSpriteNode Properties
// Creation SKSpriteNode(imageNamed: "player") // From asset catalog SKSpriteNode(texture: texture) // From SKTexture SKSpriteNode(texture: texture, size: size) // Custom size SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50)) // Solid color
// Key properties sprite.anchorPoint = CGPoint(x: 0.5, y: 0) // Bottom-center sprite.colorBlendFactor = 0.5 // Tint strength (0-1) sprite.color = .red // Tint color sprite.normalTexture = normalMap // For lighting sprite.lightingBitMask = 0x1 // Which lights affect this sprite.shadowCastBitMask = 0x1 // Which lights cast shadows sprite.shader = customShader // Per-pixel effects
SKLabelNode Properties
let label = SKLabelNode(text: "Score: 0") label.fontName = "AvenirNext-Bold" label.fontSize = 24 label.fontColor = .white label.horizontalAlignmentMode = .left label.verticalAlignmentMode = .top label.numberOfLines = 0 // Multi-line (iOS 11+) label.preferredMaxLayoutWidth = 200 label.lineBreakMode = .byWordWrapping
Part 2: Physics API
SKPhysicsBody Creation
// Volume bodies (have mass, respond to forces) SKPhysicsBody(circleOfRadius: 20) // Cheapest SKPhysicsBody(rectangleOf: CGSize(width: 40, height: 60)) SKPhysicsBody(polygonFrom: path) // Convex only SKPhysicsBody(texture: texture, size: size) // Pixel-perfect (expensive) SKPhysicsBody(texture: texture, alphaThreshold: 0.5, size: size) SKPhysicsBody(bodies: [body1, body2]) // Compound
// Edge bodies (massless boundaries) SKPhysicsBody(edgeLoopFrom: rect) // Rectangle boundary SKPhysicsBody(edgeLoopFrom: path) // Path boundary SKPhysicsBody(edgeFrom: pointA, to: pointB) // Single edge SKPhysicsBody(edgeChainFrom: path) // Open path
Physics Body Properties
// Identity body.categoryBitMask = 0x1 // What this body IS body.collisionBitMask = 0x2 // What it bounces off body.contactTestBitMask = 0x4 // What triggers didBegin/didEnd
// Physical characteristics body.mass = 1.0 // kg body.density = 1.0 // kg/m^2 (auto-calculates mass) body.friction = 0.2 // 0.0 (ice) to 1.0 (rubber) body.restitution = 0.3 // 0.0 (no bounce) to 1.0 (perfect bounce) body.linearDamping = 0.1 // Air resistance (0 = none) body.angularDamping = 0.1 // Rotational damping
// Behavior body.isDynamic = true // Responds to forces body.affectedByGravity = true // Subject to world gravity body.allowsRotation = true // Can rotate from physics body.pinned = false // Pinned to parent position body.usesPreciseCollisionDetection = false // For fast objects
// Motion (read/write) body.velocity = CGVector(dx: 100, dy: 0) body.angularVelocity = 0.0
// Force application body.applyForce(CGVector(dx: 0, dy: 100)) // Continuous body.applyImpulse(CGVector(dx: 0, dy: 50)) // Instant body.applyTorque(0.5) // Continuous rotation body.applyAngularImpulse(1.0) // Instant rotation body.applyForce(CGVector(dx: 10, dy: 0), at: point) // Force at point
SKPhysicsWorld
scene.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8) scene.physicsWorld.speed = 1.0 // 0 = paused, 2 = double speed scene.physicsWorld.contactDelegate = self
// Ray casting let body = scene.physicsWorld.body(at: point) let bodyInRect = scene.physicsWorld.body(in: rect) scene.physicsWorld.enumerateBodies(alongRayStart: start, end: end) { body, point, normal, stop in // Process each body the ray intersects }
Physics Joints
// Pin joint (pivot) let pin = SKPhysicsJointPin.joint( withBodyA: bodyA, bodyB: bodyB, anchor: anchorPoint )
// Fixed joint (rigid connection) let fixed = SKPhysicsJointFixed.joint( withBodyA: bodyA, bodyB: bodyB, anchor: anchorPoint )
// Spring joint let spring = SKPhysicsJointSpring.joint( withBodyA: bodyA, bodyB: bodyB, anchorA: pointA, anchorB: pointB ) spring.frequency = 1.0 // Oscillations per second spring.damping = 0.5 // 0 = no damping
// Sliding joint (linear constraint) let slide = SKPhysicsJointSliding.joint( withBodyA: bodyA, bodyB: bodyB, anchor: point, axis: CGVector(dx: 1, dy: 0) )
// Limit joint (distance constraint) let limit = SKPhysicsJointLimit.joint( withBodyA: bodyA, bodyB: bodyB, anchorA: pointA, anchorB: pointB )
// Add joint to world scene.physicsWorld.add(joint) // Remove: scene.physicsWorld.remove(joint)
Physics Fields
// Gravity (directional) let gravity = SKFieldNode.linearGravityField(withVector: vector_float3(0, -9.8, 0))
// Radial gravity (toward/away from point) let radial = SKFieldNode.radialGravityField() radial.strength = 5.0
// Electric field (charge-dependent) let electric = SKFieldNode.electricField()
// Noise field (turbulence) let noise = SKFieldNode.noiseField(withSmoothness: 0.5, animationSpeed: 1.0)
// Vortex let vortex = SKFieldNode.vortexField()
// Drag let drag = SKFieldNode.dragField()
// All fields share: field.region = SKRegion(radius: 100) // Area of effect field.strength = 1.0 // Intensity field.falloff = 0.0 // Distance falloff field.minimumRadius = 10 // Inner dead zone field.isEnabled = true field.categoryBitMask = 0xFFFFFFFF // Which bodies affected
Part 3: Action Catalog
Movement
SKAction.move(to: point, duration: 1.0) SKAction.move(by: CGVector(dx: 100, dy: 0), duration: 0.5) SKAction.moveTo(x: 200, duration: 1.0) SKAction.moveTo(y: 300, duration: 1.0) SKAction.moveBy(x: 50, y: 0, duration: 0.5) SKAction.follow(path, asOffset: true, orientToPath: true, duration: 2.0)
Rotation
SKAction.rotate(byAngle: .pi, duration: 1.0) // Relative SKAction.rotate(toAngle: .pi / 2, duration: 0.5) // Absolute SKAction.rotate(toAngle: angle, duration: 0.5, shortestUnitArc: true)
Scaling
SKAction.scale(to: 2.0, duration: 0.5) SKAction.scale(by: 1.5, duration: 0.3) SKAction.scaleX(to: 2.0, y: 1.0, duration: 0.5) SKAction.resize(toWidth: 100, height: 50, duration: 0.5)
Fading
SKAction.fadeIn(withDuration: 0.5) SKAction.fadeOut(withDuration: 0.5) SKAction.fadeAlpha(to: 0.5, duration: 0.3) SKAction.fadeAlpha(by: -0.2, duration: 0.3)
Composition
SKAction.sequence([action1, action2, action3]) // Sequential SKAction.group([action1, action2]) // Parallel SKAction.repeat(action, count: 5) // Finite repeat SKAction.repeatForever(action) // Infinite action.reversed() // Reverse SKAction.wait(forDuration: 1.0) // Delay SKAction.wait(forDuration: 1.0, withRange: 0.5) // Random delay
Texture & Color
SKAction.setTexture(texture) SKAction.setTexture(texture, resize: true) SKAction.animate(with: [tex1, tex2, tex3], timePerFrame: 0.1) SKAction.animate(with: textures, timePerFrame: 0.1, resize: false, restore: true) SKAction.colorize(with: .red, colorBlendFactor: 1.0, duration: 0.5) SKAction.colorize(withColorBlendFactor: 0, duration: 0.5)
Sound
SKAction.playSoundFileNamed("explosion.wav", waitForCompletion: false)
Node Tree
SKAction.removeFromParent() SKAction.run(block) SKAction.run(block, queue: .main) SKAction.customAction(withDuration: 1.0) { node, elapsed in // Custom per-frame logic }
Physics
SKAction.applyForce(CGVector(dx: 0, dy: 100), duration: 0.5) SKAction.applyImpulse(CGVector(dx: 50, dy: 0), duration: 1.0/60.0) // ~1 frame SKAction.applyTorque(0.5, duration: 1.0) SKAction.changeCharge(to: 1.0, duration: 0.5) SKAction.changeMass(to: 2.0, duration: 0.5)
Timing Modes
action.timingMode = .linear // Constant speed action.timingMode = .easeIn // Slow → fast action.timingMode = .easeOut // Fast → slow action.timingMode = .easeInEaseOut // Slow → fast → slow
action.speed = 2.0 // 2x speed
Part 4: Textures and Atlases
SKTexture
// From image let tex = SKTexture(imageNamed: "player")
// From atlas let atlas = SKTextureAtlas(named: "Characters") let tex = atlas.textureNamed("player_run_1")
// Subrectangle (for manual sprite sheets) let sub = SKTexture(rect: CGRect(x: 0, y: 0, width: 0.25, height: 0.5), in: sheetTexture)
// From CGImage let tex = SKTexture(cgImage: cgImage)
// Filtering tex.filteringMode = .nearest // Pixel art (no smoothing) tex.filteringMode = .linear // Smooth scaling (default)
// Preload SKTexture.preload([tex1, tex2]) { /* Ready */ }
SKTextureAtlas
// Create in Xcode: Assets.xcassets → New Sprite Atlas // Or .atlas folder in project bundle
let atlas = SKTextureAtlas(named: "Characters") let textureNames = atlas.textureNames // All texture names in atlas
// Preload entire atlas atlas.preload { /* Atlas ready */ }
// Preload multiple atlases SKTextureAtlas.preloadTextureAtlases([atlas1, atlas2]) { /* All ready */ }
// Animation from atlas let frames = (1...8).map { atlas.textureNamed("run_($0)") } let animate = SKAction.animate(with: frames, timePerFrame: 0.1)
Part 5: Constraints
// Orient toward another node let orient = SKConstraint.orient(to: targetNode, offset: SKRange(constantValue: 0))
// Orient toward a point let orient = SKConstraint.orient(to: point, offset: SKRange(constantValue: 0))
// Position constraint (keep X in range) let xRange = SKConstraint.positionX(SKRange(lowerLimit: 0, upperLimit: 400))
// Position constraint (keep Y in range) let yRange = SKConstraint.positionY(SKRange(lowerLimit: 50, upperLimit: 750))
// Distance constraint (stay within range of node) let dist = SKConstraint.distance(SKRange(lowerLimit: 50, upperLimit: 200), to: targetNode)
// Rotation constraint let rot = SKConstraint.zRotation(SKRange(lowerLimit: -.pi/4, upperLimit: .pi/4))
// Apply constraints (processed in order) node.constraints = [orient, xRange, yRange]
// Toggle node.constraints?.first?.isEnabled = false
SKRange
SKRange(constantValue: 100) // Exactly 100 SKRange(lowerLimit: 50, upperLimit: 200) // 50...200 SKRange(lowerLimit: 0) // >= 0 SKRange(upperLimit: 500) // <= 500 SKRange(value: 100, variance: 20) // 80...120
Part 6: Scene Setup
SKView Configuration
let skView = SKView(frame: view.bounds)
// Debug overlays skView.showsFPS = true skView.showsNodeCount = true skView.showsDrawCount = true skView.showsPhysics = true skView.showsFields = true skView.showsQuadCount = true
// Performance skView.ignoresSiblingOrder = true // Enables batching optimizations skView.shouldCullNonVisibleNodes = true // Auto-hide offscreen (manual is faster) skView.isAsynchronous = true // Default: renders asynchronously skView.allowsTransparency = false // Opaque is faster
// Frame rate skView.preferredFramesPerSecond = 60 // Or 120 for ProMotion
// Present scene skView.presentScene(scene) skView.presentScene(scene, transition: .fade(withDuration: 0.5))
Scale Mode Matrix
Mode Aspect Ratio Content Best For
.aspectFill
Preserved Fills view, crops edges Most games
.aspectFit
Preserved Fits in view, letterboxes Exact layout needed
.resizeFill
Distorted Stretches to fill Almost never
.fill
Varies Scene resizes to match view Adaptive scenes
SKTransition Types
SKTransition.fade(withDuration: 0.5) SKTransition.fade(with: .black, duration: 0.5) SKTransition.crossFade(withDuration: 0.5) SKTransition.flipHorizontal(withDuration: 0.5) SKTransition.flipVertical(withDuration: 0.5) SKTransition.reveal(with: .left, duration: 0.5) SKTransition.moveIn(with: .right, duration: 0.5) SKTransition.push(with: .up, duration: 0.5) SKTransition.doorway(withDuration: 0.5) SKTransition.doorsOpenHorizontal(withDuration: 0.5) SKTransition.doorsOpenVertical(withDuration: 0.5) SKTransition.doorsCloseHorizontal(withDuration: 0.5) SKTransition.doorsCloseVertical(withDuration: 0.5) // Custom with CIFilter: SKTransition(ciFilter: filter, duration: 0.5)
Part 7: Particles
SKEmitterNode Key Properties
let emitter = SKEmitterNode(fileNamed: "Spark")!
// Emission control emitter.particleBirthRate = 100 // Particles per second emitter.numParticlesToEmit = 0 // 0 = infinite emitter.particleLifetime = 2.0 // Seconds emitter.particleLifetimeRange = 0.5 // ± random
// Position emitter.particlePosition = .zero emitter.particlePositionRange = CGVector(dx: 10, dy: 10)
// Movement emitter.emissionAngle = .pi / 2 // Direction (radians) emitter.emissionAngleRange = .pi / 4 // Spread emitter.particleSpeed = 100 // Points per second emitter.particleSpeedRange = 50 // ± random emitter.xAcceleration = 0 emitter.yAcceleration = -100 // Gravity-like
// Appearance emitter.particleTexture = SKTexture(imageNamed: "spark") emitter.particleSize = CGSize(width: 8, height: 8) emitter.particleColor = .white emitter.particleColorAlphaSpeed = -0.5 // Fade out emitter.particleBlendMode = .add // Additive for fire/glow emitter.particleAlpha = 1.0 emitter.particleAlphaSpeed = -0.5
// Scale emitter.particleScale = 1.0 emitter.particleScaleRange = 0.5 emitter.particleScaleSpeed = -0.3 // Shrink over time
// Rotation emitter.particleRotation = 0 emitter.particleRotationSpeed = 2.0
// Target node (for trails) emitter.targetNode = scene // Particles stay in world space
// Render order emitter.particleRenderOrder = .dontCare // .oldestFirst, .oldestLast, .dontCare
// Physics field interaction emitter.fieldBitMask = 0x1
Common Particle Presets
Effect Key Settings
Fire blendMode: .add , fast alphaSpeed , orange→red color, upward speed
Smoke blendMode: .alpha , slow speed, gray color, scale up over time
Sparks blendMode: .add , high speed + range, short lifetime, small size
Rain Downward emissionAngle , narrow range, long lifetime, thin texture
Snow Slow downward speed, wide position range, slight x acceleration
Trail Set targetNode to scene, narrow emission angle, medium lifetime
Explosion High birth rate, short numParticlesToEmit , high speed range
Part 8: SKRenderer and Shaders
SKRenderer (Metal Integration)
import MetalKit
let device = MTLCreateSystemDefaultDevice()! let renderer = SKRenderer(device: device) renderer.scene = gameScene renderer.ignoresSiblingOrder = true
// In Metal render loop: func draw(in view: MTKView) { guard let commandBuffer = commandQueue.makeCommandBuffer(), let rpd = view.currentRenderPassDescriptor else { return }
renderer.update(atTime: CACurrentMediaTime())
renderer.render(
withViewport: CGRect(origin: .zero, size: view.drawableSize),
commandBuffer: commandBuffer,
renderPassDescriptor: rpd
)
commandBuffer.present(view.currentDrawable!)
commandBuffer.commit()
}
SKShader (Custom GLSL ES Effects)
// Fragment shader for per-pixel effects let shader = SKShader(source: """ void main() { vec4 color = texture2D(u_texture, v_tex_coord); // Desaturate float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); gl_FragColor = vec4(vec3(gray), color.a) * v_color_mix.a; } """)
sprite.shader = shader
// With uniforms let shader = SKShader(source: """ void main() { vec4 color = texture2D(u_texture, v_tex_coord); color.rgb *= u_intensity; gl_FragColor = color; } """) shader.uniforms = [ SKUniform(name: "u_intensity", float: 0.8) ]
// Built-in uniforms: // u_texture — sprite texture // u_time — elapsed time // u_path_length — shape node path length // v_tex_coord — texture coordinate // v_color_mix — color/alpha mix // SKAttribute for per-node values
Part 7: SwiftUI Integration
SpriteView
import SpriteKit import SwiftUI
// Basic embedding struct GameView: View { var body: some View { SpriteView(scene: makeScene()) .ignoresSafeArea() }
func makeScene() -> SKScene {
let scene = GameScene(size: CGSize(width: 1024, height: 768))
scene.scaleMode = .aspectFill
return scene
}
}
// With options SpriteView( scene: scene, transition: .fade(withDuration: 0.5), // Scene transition isPaused: false, // Pause control preferredFramesPerSecond: 60, // Frame rate options: [.ignoresSiblingOrder, .shouldCullNonVisibleNodes], debugOptions: [.showsFPS, .showsNodeCount] // Debug overlays )
SpriteView Options
Option Purpose
.ignoresSiblingOrder
Enable draw order batching optimization
.shouldCullNonVisibleNodes
Auto-hide offscreen nodes
.allowsTransparency
Allow transparent background (slower)
Debug Options
Option Shows
.showsFPS
Frames per second
.showsNodeCount
Total visible nodes
.showsDrawCount
Draw calls per frame
.showsPhysics
Physics body outlines
.showsFields
Physics field regions
.showsQuadCount
Quad subdivisions
Communicating Between SwiftUI and SpriteKit
// Observable model shared between SwiftUI and scene @Observable class GameState { var score = 0 var isPaused = false var lives = 3 }
// Scene reads/writes the shared model class GameScene: SKScene { var gameState: GameState?
override func update(_ currentTime: TimeInterval) {
guard let state = gameState, !state.isPaused else { return }
// Game logic updates state.score, state.lives, etc.
}
}
// SwiftUI view owns the model struct GameContainerView: View { @State private var gameState = GameState() @State private var scene: GameScene = { let s = GameScene(size: CGSize(width: 1024, height: 768)) s.scaleMode = .aspectFill return s }()
var body: some View {
VStack {
Text("Score: \(gameState.score)")
SpriteView(scene: scene, isPaused: gameState.isPaused)
.ignoresSafeArea()
}
.onAppear { scene.gameState = gameState }
}
}
Key pattern: Use @Observable model as bridge. Scene mutates it; SwiftUI observes changes. Avoid recreating scenes in view body — use @State to persist the scene instance.
Resources
WWDC: 2014-608, 2016-610, 2017-609
Docs: /spritekit/skspritenode, /spritekit/skphysicsbody, /spritekit/skaction, /spritekit/skemitternode, /spritekit/skrenderer
Skills: axiom-spritekit, axiom-spritekit-diag