<!--
{
  "documentType" : "article",
  "framework" : "SpriteKit",
  "identifier" : "/documentation/SpriteKit/controlling-shape-drawing-with-shaders",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Controlling Shape Drawing with Shaders"
}
-->

# Controlling Shape Drawing with Shaders

Change a shape node’s appearance by supplying custom shader code.

## Discussion

When you want to go beyond the effects provided by a shape node’s properties, you can take full control of its stroking or filling by using the [`strokeShader`](/documentation/SpriteKit/SKShapeNode/strokeShader) and [`fillShader`](/documentation/SpriteKit/SKShapeNode/fillShader) properties, respectively. To do that, you supply custom OpenGL ES shader code embedded within a [`SKShader`](/documentation/SpriteKit/SKShader) object. Custom shaders allow you to create custom effects, such as dashed lines and gradient strokes, and custom fills, such as checkerboards and random patterns.

### Customize a Shape Node’s Stroke

Shape nodes have two additional stroke-related properties that extend the properties defined by [`SKShader`](/documentation/SpriteKit/SKShader):

|Symbol declaration    |Type   |Description                             |
|----------------------|-------|----------------------------------------|
|float u_path_length;  |Uniform|The total length of the path, in points.|
|float v_path_distance;|Varying|The distance along the path, in points. |

By dividing the distance along the path by the total length of the path, you get the normalized position (between `0` and `1`) of each point along a shape node’s path and use it to construct the color of each pixel along the shape node’s stroke. The following code shows how you create a custom shader to do this:

```swift
let gradientShader = SKShader(source: "void main() {" +
    "float normalisedPosition = v_path_distance / u_path_length;" +
    "gl_FragColor = vec4(normalisedPosition, normalisedPosition, 0.0, 1.0);" +
    "}")
let squareShapeNode = SKShapeNode(rectOf: CGSize(width: 610, height: 200),
                                  cornerRadius: 25)
squareShapeNode.fillColor = .clear
squareShapeNode.lineWidth = 20
squareShapeNode.strokeShader = gradientShader
```

The generated shape node looks like this:

![Shape node with graduated stroke](images/com.apple.spritekit/media-2647280@2x.png)

Alternatively, by casting both symbols to integers and using the modulo operator, you get the same shape node with a shader that generates a dashed line, as shown in the following code:

```swift
let dashedShader = SKShader(source: "void main() {" +
    "int stripe = int(u_path_length) / 150;" +
    "int h = int(v_path_distance) / stripe % 2;" +
    "gl_FragColor = float4(h);" +
    "}")
```

The generated shape node looks like this:

![Shape node with dashed stroke](images/com.apple.spritekit/media-2647282@2x.png)

### Customize a Shape Node’s Fill

You create a custom fill for a shape node by writing shader code and embedding it within an [`SKShader`](/documentation/SpriteKit/SKShader) object. Assigning the shader to the [`fillShader`](/documentation/SpriteKit/SKShapeNode/fillShader) property overrides the appearance that would otherwise be defined by [`fillColor`](/documentation/SpriteKit/SKShapeNode/fillColor) and [`fillTexture`](/documentation/SpriteKit/SKShapeNode/fillTexture).

The following shader code demonstrates filling a shape node with a simple checkerboard texture. Inside the shader, the variables `h` and `v` would, on their own, form horizontal and vertical stripes. The exclusive or operator, `^`, creates the checkerboard pattern from those stripes.

```swift
let checkerboardShader = SKShader(source: "void main() {" +
    "int size = 20;" +
    "int h = int(v_tex_coord.x * u_texture_size.x) / size % 2;" +
    "int v = int(v_tex_coord.y * u_texture_size.y) / size % 2;" +
    "gl_FragColor = float4(v ^ h, v ^ h, v ^ h, 1.0);" +
    "}")
   
let size = CGSize(width: 610, height: 200)
   
checkerboardShader.uniforms = [
    SKUniform(name: "u_texture_size",
              vectorFloat2: vector_float2(Float(size.width), Float(size.height)))
]
   
let squareShapeNode = SKShapeNode(rectOf: size,
                                  cornerRadius: 25)
squareShapeNode.fillShader = checkerboardShader
```

The generated shape node looks like this:

![Shape node with checkerboard fill](images/com.apple.spritekit/media-2647284@2x.png)

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)