A tiny, native SwiftUI package for expressive 3×3 pixel animations on iOS and macOS.
If you find this project helpful, please consider giving it a star in the top-right corner to show your support!
Watch the full-quality MP4 demo
Install this package for me: afetmin/SwiftPixelGrid
In Xcode, choose File → Add Package Dependencies, then enter:
https://github.com/afetmin/SwiftPixelGrid
Select Up to Next Major Version starting from 0.1.0, then add the
SwiftPixelGrid library to your app target.
dependencies: [
.package(
url: "https://github.com/afetmin/SwiftPixelGrid.git",
from: "0.1.0"
)
]Add the product to your target:
.target(
name: "YourApp",
dependencies: ["SwiftPixelGrid"]
)import SwiftPixelGrid
import SwiftUI
struct LoadingView: View {
var body: some View {
PixelGrid(
preset: .ember,
bloom: .standard,
scale: 5,
accessibilityLabel: "Loading"
)
}
}PixelGrid is the recommended entry point. It renders with one asynchronous
Canvas and supports both animation engines:
- Continuous delay animations from
PixelGridAnimationorPixelGridPreset - Discrete frame animations from
PixelPattern
Choose one of the 24 built-in presets:
PixelGrid(
preset: .prism,
bloom: PixelGridBloom(amount: 4, intensity: 0.3),
cornerRadius: 0.5,
scale: 6
)Create a custom animation with one delay per cell:
let animation = try PixelGridAnimation(
name: "warm-spiral",
delays: [
0, 0.08, 0.16,
0.56, 0.64, 0.24,
0.48, 0.40, 0.32,
],
duration: 0.18,
color: try PixelGridColor(hex: "#FFD7B0")
)
PixelGrid(animation: animation, bloom: .standard, scale: 5)Override the color of any preset:
let cyanSpiral = PixelGridPreset.spiralClockwise.animation.withColor(.cyan)
PixelGrid(animation: cyanSpiral, scale: 5)Cells use this fixed numbering:
1 2 3
4 5 6
7 8 9
Create a pattern and render it:
let ring = PixelPattern([[1, 2, 3, 6, 9, 8, 7, 4]])
PixelGrid(
pattern: ring,
frameDuration: 0.1,
transitionDuration: 0.08,
color: .cyan,
bloom: .standard,
scale: 5
)Pattern groups compile as follows:
[[1, 2, 3]]becomes[[1], [2], [3]]for sequential playback.[[2], [4], [6], [8]]becomes[[2, 4, 6, 8], []]for group blinking.[[1, 2], [5], [7, 8, 9]]stays as three explicit frames.
Values outside 1...9 and duplicate values are ignored. Each frame is
normalized to natural grid order.
Pause without removing the view:
PixelGrid(
preset: .aurora,
isAnimating: isAnimating,
accessibilityLabel: "Syncing"
)The package automatically follows the system Reduce Motion preference. When Reduce Motion is enabled, the animation becomes a meaningful static frame and the display timeline stops refreshing.
Useful appearance options:
PixelGrid(
preset: .frost,
bloom: PixelGridBloom(amount: 3, intensity: 0.24),
cornerRadius: 1,
isAnimating: true,
scale: 7,
accessibilityLabel: "Processing"
)scalechanges the grid, cell glow, and Bloom radius together.cornerRadiusis clamped to the cell geometry.bloomadds an optional glow on top of the built-in two-layer cell glow..disabledand.standardprovide ready-to-use Bloom configurations.
PixelPatternView is an alternative renderer for Pattern animations. It uses
nine native SwiftUI child views instead of Canvas and exposes explicit cell
size, spacing, rotation, and tile size controls:
PixelPatternView(
brightness: 2,
bloom: .standard,
color: .orange,
cornerRadius: 2,
rotation: 0,
tileSize: 92,
pixelSize: 12,
spacing: 3,
frameDuration: 0.12,
transitionDuration: 0.1,
pattern: PixelPattern([[1, 5, 9], [3, 5, 7]]),
accessibilityLabel: "Pattern animation"
)Use PixelGrid for continuous delay animations and as the default choice for
Pattern animations.
Both time engines can be used without SwiftUI.
Continuous delay engine:
let delayEngine = PixelDelayEngine(
animation: PixelGridPreset.waveLeftToRight.animation
)
let delayIntensities = delayEngine.intensities(at: elapsedTime)Pattern engine:
let patternEngine = PixelPatternEngine(
pattern: PixelPattern([[1, 2, 3]]),
frameDuration: 0.1,
transitionDuration: 0.08
)
let activeCells = patternEngine.activeCells(at: elapsedTime)
let patternIntensities = patternEngine.intensities(at: elapsedTime)Both intensities methods return nine values in row-major order. Pass
isAnimating: false or reduceMotion: true when integrating the engines into
a custom renderer.
| Requirement | Minimum |
|---|---|
| iOS | 17.0 |
| macOS | 14.0 |
| Xcode | 16.0 |
| Swift | 6.0 |
The package has no third-party runtime dependencies.
Open the checked-in multiplatform example:
open Examples/PixelGridDemo/PixelGridDemo.xcodeprojIt runs on both iOS Simulator and macOS and references the local package.
The package also includes a macOS executable gallery:
swift run PixelGridDemoUse AI 配置 to view the current values and copy an AI-ready prompt.
Run the logic test suite:
swift testThe repository CI runs the logic tests on macOS and compiles the library and example app for iOS Simulator. The project intentionally contains no UI tests.
Continuous animations keep one start time and calculate all nine intensities
with pure functions. A single TimelineView drives one asynchronous Canvas.
Paused and reduced-motion states stop frame-by-frame refresh.
Pattern playback maps absolute time directly to normalized frames. SwiftUI interpolates the nine-value frame vector, so an interrupted transition continues from its current visual state.
The full algorithm description is available in Core Algorithms.
MIT © 2026 Ranxiu. See LICENSE.