axiom-spritekit-ref

SpriteKit API Reference

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "axiom-spritekit-ref" with this command: npx skills add charleswiltgen/axiom/charleswiltgen-axiom-axiom-spritekit-ref

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

axiom-vision

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftdata

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftui-26-ref

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftui-architecture

No summary provided by upstream source.

Repository SourceNeeds Review