The SKNode class is the fundamental building block of most SpriteKit content.


The SKNode class doesn’t draw any visual content. Its primary role is to provide baseline behavior that the other node classes use. All visual elements in a SpriteKit-based game are drawn using predefined SKNode subclasses.

Characteristics of Nodes

Nodes are organized hierarchically into node trees, similar to how views and subviews work. Most commonly, a node tree is defined with a scene node (SKScene) as the root node and other content nodes as descendants. The scene node runs an animation loop that processes actions on the nodes, simulates physics, and then renders the contents of the node tree for display.

Every node in a node tree provides a coordinate system to its children. After a child is added to the node tree, it is positioned inside its parent’s coordinate system by setting its position properties. A node’s coordinate system can be scaled and rotated by changing its xScale, yScale, and zRotation properties. When a node’s coordinate system is scaled or rotated, this transformation is applied both to the node’s own content and to that of its descendants.

The SKNode class does not perform any drawing of its own. However, many SKNode subclasses render visual content and so the SKNode class understands some visual concepts:

  • The frame property provides the bounding rectangle for a node’s visual content, modified by the scale and rotation properties. The frame is non-empty if the node’s class draws content. Each node subclass determines the size of this content differently. In some subclasses, the size of the node’s content is declared explicitly, such as in the SKSpriteNode class. In other subclasses, the content size is calculated implicitly by the class using other object properties. For example, an SKLabelNode object determines its content size using the label’s message text and font characteristics.

  • A node’s accumulated frame, retrieved by calling the calculateAccumulatedFrame() method, is the largest rectangle that includes the frame of the node and the frames of all its descendants.

  • Other properties, such as the alpha and isHidden properties, affect how the node and its descendants are drawn.

All nodes are responder objects that can respond directly to user interaction with the node onscreen. You can also convert between coordinate systems and perform hit testing to determine which nodes a point lies in. You can also perform intersections between nodes in the tree to determine if their physical areas overlap.

Any node in the tree may run actions, which are used to animate the properties of a node, add or remove nodes, play sounds, or perform other custom tasks. Actions are the heart of the animation system in SpriteKit.

A node can support a physics body, which is an object that simulates the physical properties of the object. When a node has a physics body, the physics simulation automatically computes a new position for the physics body and then moves and rotates the node to match that position.

A node can supply constraints that express relationships with other nodes or locations in the scene. These constraints are automatically applied by the scene before the scene is rendered. Another set of constraints is used in conjunction with actions to perform inverse-kinematic animations.

Manipulations to nodes must occur in the main thread. All of the SpriteKit callbacks for SKViewDelegate, SKSceneDelegate and SKScene occur in the main thread and these are safe places to make manipulations to nodes. However, if you are performing other work in the background, you should adopt an approach similar to Listing 1.

Listing 1

Manipulating a node in an ordained callback

class ViewController: UIViewController {
    let queue =
    var makeNodeModifications = false
    func backgroundComputation() {
        queue.async {
            // Perform background calculations but do not modify 
            // SpriteKit objects
            // Set a flag for later modification within the 
            // SKScene or SKSceneDelegate callback of your choosing
            self.makeNodeModifications = true
extension ViewController: SKSceneDelegate {
    func update(_ currentTime: TimeInterval, for scene: SKScene) {
        if makeNodeModifications {
            makeNodeModifications = false
            // Make node modifications

Use Plain Nodes to Organize Your Game Content

Even though SKNode objects cannot directly draw content, there are useful ways to use them in your game. Here are some ideas to get you started:

  • You have content that is built up from multiple node objects, either sprites or other content nodes. However, you want this content to be thought of as a single object within your game, without promoting any one of the content nodes to be the root. A basic node is appropriate, because you can give it a position in the scene tree and then make all of the other nodes its descendants. These individual pieces can also be moved or adjusted relative to the parent’s location.

  • Use node objects to organize your drawn content into a series of layers. For example, many games have a background layer for the world, another layer for characters, and a third layer for text and other game information. Other games have many more layers. Create the layers as basic nodes, and insert them into the scene in order. Then, as necessary, you can make individual layers visible or invisible.

  • You need an object in the scene that is invisible but that performs some other necessary function. For example, in a dungeon exploring game, an invisible node might be used to represent a hidden trap. When another node intersects it, the trap is triggered. (See SKNode.) Or for another example, you might add a node as a child of another node that represents the position of the player’s point of view. See SKNode.

There are advantages to having a node in the tree to represent these concepts:

  • You can add or remove entire subtrees by adding or removing a single node. This makes scene management efficient.

  • You can adjust properties of a node in the tree and have the effects of those properties propagate down to the node’s descendants. For example, if the basic node has sprite nodes as its children, rotating the basic node rotates all of the sprite content also.

  • You can take advantage of actions, physics contacts, and other SpriteKit features to implement the concept.

A Node Provides a Coordinate System to Its Children

When a node is placed in the node tree, its position property places it within a coordinate system provided by its parent. SpriteKit uses the same coordinate system on both iOS and macOS. Figure 1 shows the SpriteKit coordinate system. Coordinate values are measured in points, as in UIKit or AppKit; where necessary, points are converted to pixels when the scene is rendered. A positive x coordinate goes to the right and a positive y coordinate goes up the screen.

Figure 1

SpriteKit coordinate system

SpriteKit coordinate system

SpriteKit also has a standard rotation convention. Figure 2 shows the polar coordinate convention. An angle of 0 radians specifies the positive x axis. A positive angle is in the counterclockwise direction.

Figure 2

Polar coordinate conventions (rotation)

Polar coordinate conventions (rotation)

Nodes are rotated by setting their zRotation property to the required angle in radians. If you prefer to work in degrees, the following code shows how you can write an extension to CGFloat that converts between the two. The example in Listing 2 rotates spriteNode by 30 degrees counterclockwise.

extension CGFloat {
    func degreesToRadians() -> CGFloat {
        return self * CGFloat.pi / 180

let rabbitTexture = SKTexture(imageNamed: "rabbit.png")

let spriteNode = SKSpriteNode(texture: rabbitTexture)

spriteNode.zRotation = CGFloat(30).degreesToRadians()

Creating the Node Tree

You create the node tree by creating parent-child relationships between nodes. Each node maintains an ordered list of children, referenced by reading the node’s children property. The order of the children in the tree affects many aspects of scene processing, including hit testing and rendering. So, it is important to organize the node tree appropriately.




Adds a node to the end of the receiver’s list of child nodes.


Inserts a child into a specific position in the receiver’s list of child nodes.


Removes the receiving node from its parent.

When you need to directly traverse the node tree, you use the properties in the following table to uncover the tree’s structure.




The array of SKNode objects that are the receiving node’s children.


If the node is a child of another node, this holds the parent. Otherwise, it holds nil.


If the node is included anywhere in a scene, this returns the scene node that is the root of the tree. Otherwise it holds nil.

Understanding the Drawing Order for a Node Tree

The standard behavior for scene rendering follows a simple pair of rules:

  • A parent draws its content before rendering its children.

  • Children are rendered in the order in which they appear in the child array.

Figure 3 shows how a node with four children are rendered.

Figure 3

Parents are drawn before children

Parents are drawn before children

In the image above, the helicopter body and its components are all immediate children of the sky node. So the scene rendered its content as follows:

  1. The scene renders itself, clearing its contents to its background color.

  2. The scene renders the sky node.

  3. The sky node renders its children, which are the helicopter body and its components, in the order they were added as children.

Maintaining the order of a node’s children can be a lot of work. Instead, you can give each node an explicit height in the scene. You do this by setting a node’s zPosition property. The z-position is the node’s height relative to its parent node, much as a node’s position property represents its x and y position relative to parent’s position. So you use the z-position to place a node above or below the parent’s position.

When you take z-positions into account, here is how the node tree is rendered:

  1. Each node’s global z-position is calculated by recursively adding its z-position to its parent's z-position.

  2. Nodes are drawn in order from smallest z-position to largest z-position.

  3. If two nodes share the same z-position, ancestors are rendered first, and siblings are rendered in child order.

As you’ve just seen, SpriteKit uses a deterministic rendering order based on the height nodes and their positions in the node tree. But, because the rendering order is so deterministic, SpriteKit may be unable to apply some rendering optimizations that it might otherwise apply. For example, it might be better if SpriteKit could gather all of the nodes that share the same texture and drawing mode and and draw them with a single drawing pass. To enable these sorts of optimizations, you set the view’s ignoresSiblingOrder property to true.

When you ignore sibling order, SpriteKit uses the graphics hardware to render the nodes so that they appear sorted by z-position. It sorts nodes into a drawing order that reduces the number of draw calls needed to render the scene. But with this optimized drawing order, you cannot predict the rendering order for nodes that share the same height. The rendering order may change each time a new frame is rendered. In many cases, the drawing order of these nodes is not important. For example, if the nodes are at the same height but do not overlap on screen, they can be drawn in any order.

Figure 4 shows an example of a tree that uses z-positions to determine the rendering order. In this example, the body of the helicopter is at a height of 100, and its children are rendered relative to its height. The two rotor nodes share the same height but do not overlap.

Figure 4

Depth-only rendering can improve performance

Depth-only rendering can improve performance

Because a child node's z-position is added to its parent's z-position, you can interleave child nodes from different parent nodes. Listing 3 shows code that creates two square parent nodes, each with a circular child node. The first node has a z-position of 10 and its child node has a z-position of 10. The second node has a z-position of 15 and its child node also has a z-position of 10.

Once added to the scene, the child of the first node has an effective global z-position of 20 and the child of the second node has an effective global z-position of 25 giving an interleaving effect.

Listing 3

Interleaving composite nodes

func addNodesTo(scene: SKScene,
                color: SKColor, position: CGPoint,
                parentZ: CGFloat, childZ: CGFloat) {
    let diameter: CGFloat = 250
    let rect = CGRect(origin: position,
                      size: CGSize(width: diameter, height: diameter))
    let parentNode = SKShapeNode(rect: rect)
    let childNode = SKShapeNode(ellipseIn: rect.insetBy(dx: 10, dy: 10))
    [(parentNode, scene, parentZ), (childNode, parentNode, childZ)].forEach {
        node, parent, zPosition in
        node.fillColor = color
        node.strokeColor = .white
        node.zPosition = zPosition

addNodesTo(scene: scene,
           color: .red,
           position: CGPoint(x: 300, y: 300),
           parentZ: 10,
           childZ: 10)
addNodesTo(scene: scene,
           color: .blue,
           position: CGPoint(x: 350, y: 250),
           parentZ: 15,
           childZ: 10)

Figure 5 shows the resulting scene.

Figure 5

Interleaved node hierarchies using z-position

Interleaved node hierarchies using z-position

To summarize, you can use both tree order and z-positions to determine your scene’s rendering order. When rendering a complex scene, you should disable the sorting behavior and use the z-positions of nodes to create a deterministic scene order.

The Hit-Testing Order Is the Reverse of Drawing Order

In a scene, when SpriteKit processes touch or mouse events, it walks the scene to find the closest node that wants to accept the event. If that node doesn’t want the event, SpriteKit checks the next closest node, and so on. The order in which hit-testing is processed is essentially the reverse of drawing order.

For a node to be considered during hit-testing, its isUserInteractionEnabled property must be set to a true. The default value is a false for any node except a scene node. A node that wants to receive events needs to implement the appropriate responder methods from its parent class (UIResponder on iOS and NSResponder on macOS). This is one of the few places where you must implement platform-specific code in SpriteKit.

Sometimes, you also want to look for nodes directly, rather than relying on the standard event-handling mechanisms. In SpriteKit you can ask a node whether any of its descendants intersect a specific point in their coordinate system. Call the atPoint(_:) method to find the first descendant that intersects the point, or use the nodes(at:) method to receive an array of all of the nodes that intersect the point.

Using a Node’s Depth to Add Other Effects

SpriteKit uses the zPosition value only to determine the hit testing and drawing order. You can also the z position to implement your own game effects. For example, you might use the height of a node to determine how it is rendered or how it moves onscreen. In this way, you can simulate fog or parallax effects. SpriteKit does not create these effects for you. Usually, you implement them by processing the scene immediately before it is rendered.

Searching the Node Tree

The nodes in the scene tree are often organized to determine the precise rendering order for the scene, not the role these nodes play in your scene. In addition, you may also be loading nodes from an archive file rather than instantiating them directly at runtime. Because of this, you may need to be able to find specific nodes within a node tree. To do this, you provide names to nodes and then search for those names.

A node’s name property holds a node’s name. The name should be an alphanumeric string without any punctuation. Listing 4 shows how you might name three different nodes to distinguish them from each other. = "player" = "goblin" = "ogre"

When you name nodes in the tree, decide whether those names will be unique. If a node’s name is unique, you should never include more than one node with that name in the scene tree. On the other hand, if a node name is not unique within your game, it might represent a collection of related nodes. For example, in Listing 4, there are probably multiple goblins within the game, and you might want to identify them all with the same name. But the player might be a unique node within the game.

The node name usually serves two purposes in your app:

  • You can write your own code that implements game logic based on the node’s name. For example, when two physics objects collide, you might use the node names to determine how the collision affects gameplay.

  • You can search for nodes that have a particular name. Typically, this is done once when a scene is first loaded.

The SKNode class implements the following methods for searching the node tree:

  • The childNode(withName:) method searches a node’s children until it finds a matching node, then it stops and returns this node. This method is usually used to search for nodes with unique names.

  • The enumerateChildNodes(withName:using:) method searches a node’s children and calls your block once for each matching node it finds. You use this method when you want to find all nodes that share the same name.

  • The subscript(_:) method returns an array of nodes that match a particular name.

Listing 5The following code shows how you might create a method on your scene class to find the player node. You might use a method like this inside your code that loads and prepares a scene.

var playerNode: SKNode? {
    return childNode(withName: "player")

When this method is called on the scene, the scene searches its children (and only its children) for a node whose name property matches the search string, then returns the node. When specifying a search string, you can either specify the name of the node or a class name. For example, if you create your own subclass for the player node and name it PlayerSprite, then you could specify PlayerSprite as the search string instead of player; the same node would be returned.

Advanced Searches

The default search only searches a node’s children and must exactly match either the node’s name or its class. However, SpriteKit provides an expressive search syntax so that you can perform more advanced searches. For example, you could do the same search as before but search the entire scene tree. Or you could search the node’s children, but match a pattern rather than requiring an exact match.

Table 3 describes the different syntax options. The search uses common regular expression semantics.

Table 3

Search syntax options




When placed at the start of the search string, this indicates that the search should be performed on the tree’s root node. When placed anywhere but the start of the search string, this indicates that the search should move to the node’s children.


When placed at the start of the search string, this specifies that the search should begin at the root node and be performed recursively across the entire node tree. Otherwise, it performs a recursive search from its current position.


Refers to the current node.


The search should move up to the node’s parent.


The search matches zero or more characters.

[characters delimited by commas or dashes]

The search matches any of the characters contained inside the brackets.

alphanumeric characters

The search matches only the specified characters.

Table 4 shows some useful search strings to help get you started.

Table 4

Example searches

Search string



This search string matches any of the current node’s children that are named MyName.


This search string matches any of the root node’s children that are named MyName.


This search string matches any nodes in the tree that are named MyName.


This search string matches any descendants of the current node that are named MyName.


This search string finds the parent nodes of any nodes in the tree that are named MyName.


This search string matches every node in the tree.


This search string matches any of the current node’s children that are named A0, A1, …, A9.


This search string matches any grandchild of the current node when the grandchild is named Normal and the grandchild’s parent is named Abby.


This search string matches matches any nodes in the tree that are named Normal and whose parents are named Abby.

A Node Applies Many of Its Properties to Its Descendants

When you change a node’s property, often the effects are propagated to the node’s descendants. The net effect is that a child is rendered based not only on its own properties but also on the properties of its ancestors.



xScale, yScale

The node’s coordinate system is scaled by these two factors. This property affects coordinate conversion, the node’s frame, drawing, and hit testing. Its descendants are similarly scaled.


The node’s coordinate system is rotated. This property affects coordinate conversion, the node’s frame, drawing, and hit testing. Its descendants are similarly scaled.


If the node is rendered using a blend mode, the alpha value is multiplied into any alpha value before the blend operation takes place. The descendants are similarly affected.


If a node is hidden, the node and its descendants are not rendered.


The speed at which a node processes actions is multiplied by this value. The descendants are similarly affected.

Converting Between Coordinate Spaces

When working with the node tree, sometimes you need to convert a position from one coordinate space to another. For example, when specifying joints in the physics system, the joint positions are specified in scene coordinates. So, if you have those points in a local coordinate system, you need to convert them to the scene’s coordinate space.

The following code shows how to convert a node’s position into the scene coordinate system. The scene is asked to perform the conversion. Remember that a node’s position is specified in its parent’s coordinate system, so the code passes node.parent as the node to convert from. You could perform the same conversion in reverse by calling the convert(_:to:) method.

let positionInScene: CGPoint?

if let parent = node.parent {
    positionInScene = node.scene?.convert(node.position,
                                          from: parent)
else {
    positionInScene = nil

One situation where you need to perform coordinate conversions is when you perform event handling. Mouse and touch events need to be converted from window coordinates to view coordinates, and from there into the scene. To simplify the code you need to write, SpriteKit adds a few convenience methods:

Subclassing Notes

Subclassing the SKNode class is a useful way to build up more complex behaviors in your game. See Use Subclassing to Create Your Own Node Behaviors.

If you add properties to a subclass and that subclass needs to be archived, the NSCoding protocol needs to be implemented on your subclasses. See Archives and Serializations Programming Guide.

Unlike views, you cannot create SKNode subclasses that perform custom drawing. To implement this kind of behavior you need to work with existing classes in SpriteKit. For example, you might create a hierarchy of nodes that each perform some of the rendering behavior you need, or you might use node classes that support custom graphics shaders and implement your graphical effects in a shader.

In many cases, expect to add methods that can be called during the scene’s pre-processing and post-processing steps. Your scene coordinates these steps, but focused node subclasses perform the work.

If you want to implement event handling in a node class, you must implement separate event-handling code for iOS and macOS. The SKNode class inherits from NSResponder on macOS and UIResponder on iOS.

In some game designs, you can rely on the fact that a particular combination of classes is always going to be used together in a specific scene. In other designs, you may want to create classes that can be used in multiple scenes. When two classes are dependent on each other, use delegation to break that dependency. Most often, you do this by defining a delegate on your node and a protocol for delegates to implement. Your scene (or another node, such as the node’s parent) implements this protocol. Your node class can then be reused in multiple scenes, without needing to know the scene’s class.

Keep in mind that when a node class is being initialized, it is not yet in the scene, so its parent and scene properties are nil. You may need to defer some initialization work until after the node is added to the scene.


Creating a New Node

init?(fileNamed: String)

Creates a new node by loading an archive file from the game’s main bundle.

Inspecting the Node’s Position

func calculateAccumulatedFrame()

Calculates a rectangle in the parent’s coordinate system that contains the content of the node and all of its descendants.

var frame: CGRect

A rectangle in the parent’s coordinate system that contains the node’s content, ignoring the node’s children.

var position: CGPoint

The position of the node in its parent's coordinate system.

var zPosition: CGFloat

The height of the node relative to its parent.

Setting a Node’s Scaling and Rotation

func setScale(CGFloat)

Sets the xScale and yScale properties of the node.

var xScale: CGFloat

A scaling factor that multiplies the width of a node and its children.

var yScale: CGFloat

A scaling factor that multiplies the height of a node and its children.

var zRotation: CGFloat

The Euler rotation about the z axis (in radians).

Inspecting a Node’s Visibility

var alpha: CGFloat

The transparency value applied to the node’s contents.

var isHidden: Bool

A Boolean value that determines whether a node and its descendants are rendered.

Determining Whether a Node Supports User Interaction

var isUserInteractionEnabled: Bool

A Boolean value that indicates whether the node receives touch events.

Working with Node Trees

func addChild(SKNode)

Adds a node to the end of the receiver’s list of child nodes.

func insertChild(SKNode, at: Int)

Inserts a child into a specific position in the receiver’s list of child nodes.

func isEqual(to: SKNode)

Compares the parameter node to the receiving node.

func move(toParent: SKNode)

Moves the node to a new parent node in the scene.

func removeFromParent()

Removes the receiving node from its parent.

func removeAllChildren()

Removes all of the node’s children.

func removeChildren(in: [SKNode])

Removes a list of children from the receiving node.

func inParentHierarchy(SKNode)

Returns a Boolean value that indicates whether the node is a descendant of the target node.

var children: [SKNode]

The node’s children.

var parent: SKNode?

The node’s parent node.

var scene: SKScene?

The scene node that contains the node.

Naming Nodes

func childNode(withName: String)

Searches the children of the receiving node for a node with a specific name.

func enumerateChildNodes(withName: String, using: (SKNode, UnsafeMutablePointer<ObjCBool>) -> Void)

Search the children of the receiving node to perform processing for nodes which share a name.


Returns an array of nodes that match the name parameter.

var name: String?

The node’s assignable name.

Running Actions

func run(SKAction)

Adds an action to the list of actions executed by the node.

func run(SKAction, completion: () -> Void)

Adds an action to the list of actions executed by the node.

func run(SKAction, withKey: String)

Adds an identifiable action to the list of actions executed by the node.

func action(forKey: String)

Returns an action associated with a specific key.

func hasActions()

Returns a Boolean value that indicates whether the node is executing actions.

func removeAllActions()

Ends and removes all actions from the node.

func removeAction(forKey: String)

Removes an action associated with a specific key.

var speed: CGFloat

A speed modifier applied to all actions executed by a node and its descendants.

var isPaused: Bool

A Boolean value that determines whether actions on the node and its descendants are processed.

Adding Physics to a Node

var physicsBody: SKPhysicsBody?

The physics body associated with the node.

Converting to and from the Node’s Coordinate System

func convert(CGPoint, from: SKNode)

Converts a point from the coordinate system of another node in the node tree to the coordinate system of this node.

func convert(CGPoint, to: SKNode)

Converts a point in this node’s coordinate system to the coordinate system of another node in the node tree.

Determining If a Point Lies in a Node

func contains(CGPoint)

Returns a Boolean value that indicates whether a point lies inside the parent’s coordinate system.

func atPoint(CGPoint)

Returns the deepest visible descendant that intersects a point.

func nodes(at: CGPoint)

Returns an array of all visible descendants that intersect a point.

Performing Node Intersections

func intersects(SKNode)

Returns a Boolean value that indicates whether this node intersects the specified node.

Creating GameplayKit Obstacles from a Set of Nodes

Often, the pathfinding in your game should closely match its visual representation. These methods provide ways to quickly convert the visual representation of your scene into obstacles that can be used with GameplayKit.

class func obstacles(fromNodeBounds: [SKNode])

Converts each node into an obstacle by transforming its bounds into the scene’s coordinate system.

class func obstacles(fromNodePhysicsBodies: [SKNode])

Converts each node into an obstacle by transforming the node’s physics body shape into the scene’s coordinate system.

class func obstacles(fromSpriteTextures: [SKNode], accuracy: Float)

Converts each node into an obstacle by first transforming the node’s texture into a physics shape and then converting that shape into the scene’s coordinate system.

Working with GameplayKit Entities

var entity: GKEntity?

The GameplayKit entity this node represents.

Storing Custom Node Data

var userData: NSMutableDictionary?

A dictionary containing arbitrary data.

Constraining a Node’s Behavior Relative to Other Nodes

var constraints: [SKConstraint]?

Specifies the list of constraints to apply to the node.

var reachConstraints: SKReachConstraints?

Specifies the reach constraints to apply to the node when executing a reach action.


enum SKBlendMode

The modes that describe how the source and destination pixel colors are used to calculate the new destination color.

Instance Methods

func setValue(SKAttributeValue, forAttribute: String)

Sets an attribute value for an attached shader

func value(forAttributeNamed: String)

The value of a shader attribute.


See Also

Displaying SpriteKit Content in Your App

class SKView

An object that displays SpriteKit content. This content is provided by an SKScene object.

class SKScene

The root node for all Sprite Kit objects displayed in a view.

protocol SKViewDelegate

Methods that allow dynamic control of an SKView object's render rate.

protocol SKSceneDelegate

Methods your app can implement to participate in SpriteKit's animation loop.

Beta Software

This documentation contains preliminary information about an API or technology in development. This information is subject to change, and software implemented according to this documentation should be tested with final operating system software.

Learn more about using Apple's beta software