ColorfulX is a Metal-backed gradient renderer for Apple platforms. It combines LAB color interpolation, spring-based animation, and a configurable compute pipeline to produce vivid multicolor backgrounds from SwiftUI, UIKit, and AppKit.
- Metal compute shaders render up to eight color stops with LAB interpolation for smooth transitions.
AnimatedMulticolorGradientViewdrives time-based updates with frame limiting, noise, bias, and transition controls.ColorfulViewexposes the renderer to SwiftUI using familiar bindings, whileMulticolorGradientcovers static gradients.- Built-in presets (
ColorfulPreset) and theColorfulColorsprotocol make it easy to capture repeatable palettes. - Example app (
Example/ColorfulApp) showcases live controls for every parameter, including frame limiting and render scaling.
- Swift 5.9 or later (
// swift-tools-version: 5.9inPackage.swift). - iOS 13+, macOS 11+, macCatalyst 13+, tvOS 13+, visionOS 1+ (see
Package.swift). - A device or simulator with Metal support.
Add ColorfulX to your package dependencies:
.package(url: "https://github.com/Lakr233/ColorfulX.git", from: "5.8.0")Add ColorfulX to any target that needs the library:
.target(
name: "MyApp",
dependencies: ["ColorfulX"]
)Xcode users can also add the package through File → Add Packages… and paste the repository URL.
- Open
Example/ColorfulApp.xcodeprojor the workspace insideExample/ColorfulApp.xcworkspace. - Run the ColorfulApp scheme on your preferred device or simulator.
- Use the control panel to tweak preset selection, speed, bias, noise, transition speed, frame limit, and render scale. The chessboard overlay (
ChessboardView) helps visualise transparency on visionOS/macOS.
You do import ColorfulX and then ColorfulView(color: .aurora). Done.
import ColorfulX
import SwiftUI
struct AnimatedGradientDemo: View {
@State private var preset: ColorfulPreset = .aurora
@State private var speed: Double = 1.0
@State private var bias: Double = 0.01
@State private var noise: Double = 8.0
@State private var transition: Double = 3.5
@State private var frameLimit: Int = 60
@State private var renderScale: Double = 1.0
var body: some View {
ColorfulView(
color: $preset,
speed: $speed,
bias: $bias,
noise: $noise,
transitionSpeed: $transition,
frameLimit: $frameLimit,
renderScale: $renderScale
)
.ignoresSafeArea()
}
}ColorfulView also accepts bindings to [Color], constant presets (ColorfulPreset), or any custom type conforming to ColorfulColors. The animationDirector parameter allows customizing animation behavior (e.g., using SpeckleAnimationRoundedRectangleDirector for path-based movement). Internally the view converts data to ColorVector instances and feeds AnimatedMulticolorGradientViewRepresentable.
Generally, we recommend using ColorfulView with speed set to 0 for that.
For non-animated backgrounds use MulticolorGradient (SwiftUI wrapper for MulticolorGradientView):
import ColorfulX
import SwiftUI
import ColorVector
struct StaticGradientDemo: View {
private let parameters = MulticolorGradientView.Parameters(
points: [
.init(
color: ColorVector(UIColor.systemPink, usingSpace: .lab),
position: .init(x: 0.0, y: 0.0)
),
.init(
color: ColorVector(UIColor.systemBlue, usingSpace: .lab),
position: .init(x: 1.0, y: 1.0)
)
],
bias: 0.01,
power: 4,
noise: 0
)
var body: some View {
MulticolorGradient(parameters: parameters)
.ignoresSafeArea()
}
}The underlying shader supports up to eight color stops (Uniforms.COLOR_SLOT). When fewer colors are supplied, the view can repeat stops to fill the pipeline.
import ColorfulX
let animatedView = AnimatedMulticolorGradientView()
animatedView.setColors(ColorfulPreset.aurora)
animatedView.speed = 1.2
animatedView.bias = 0.01
animatedView.noise = 12
animatedView.transitionSpeed = 4.0
animatedView.frameLimit = 60
animatedView.renderScale = 1.0setColors accepts ColorfulPreset, any ColorfulColors value, [ColorVector], or [ColorElement] (UIColor / NSColor). Pass animated: false to swap palettes instantly or repeats: false to avoid repeating colors when providing fewer than eight stops.
import ColorfulX
import ColorVector
let staticView = MulticolorGradientView()
staticView.parameters = .init(
points: [
.init(color: ColorVector(UIColor.systemOrange, usingSpace: .lab), position: .init(x: 0.0, y: 0.0)),
.init(color: ColorVector(UIColor.systemTeal, usingSpace: .lab), position: .init(x: 1.0, y: 0.5)),
.init(color: ColorVector(UIColor.systemPurple, usingSpace: .lab), position: .init(x: 0.2, y: 1.0))
],
bias: 0.015,
power: 4,
noise: 0
)MulticolorGradientView renders immediately when parameters change and respects renderScale (via metalLink.scaleFactor) for performance tuning.
- Built-in palettes live in
Sources/ColorfulX/ColorfulPreset.swift. Each case conforms toColorfulColors, exposes a human-readablehint, and maps to up to eight LAB colors. - Create custom palettes by conforming to
ColorfulColors:
enum MarketingTheme: ColorfulColors {
case hero
var colors: [ColorElement] {
[
make(227, 108, 155),
make(134, 90, 214),
make(73, 204, 236),
make(35, 219, 167)
]
}
}- Use the same enum with SwiftUI (
ColorfulView(color: MarketingTheme.hero)) or UIKit/AppKit (animatedView.setColors(MarketingTheme.hero)).
| Parameter | Applies To | Description |
|---|---|---|
speed |
Animated views | Scales how quickly speckles traverse the canvas (moveDelta in AnimatedMulticolorGradientView+Update). |
bias |
Animated & static | Controls gradient spread (passed to shader bias). Lower values harden the gradient shape; typical range 0.00001 ... 0.01. |
power |
Static (MulticolorGradientView.Parameters) |
Shapes falloff curve; default is 4. |
noise |
Animated & static | Adds procedural noise in the shader; higher values cost more GPU time. |
transitionSpeed |
Animated views | Determines how fast colors interpolate when palettes change. |
frameLimit |
Animated views | Caps rendering frequency without altering vsync scheduling. Use 0 for unlimited. |
renderScale |
Animated & static | Adjusts Metal drawable scale; lowering improves performance at the cost of resolution. |
repeats |
Animated views | Fills unused color slots by repeating colors; disable when the number of stops is stable. |
Tips:
- Use lower
frameLimitorrenderScaleon battery-constrained devices. - Keep the number of colors ≤ 8 to avoid truncation.
- Set
speedto0for a frozen animated view, or rely onMulticolorGradientfor static scenes.
- No animation appearance – verify
speed > 0and that the view remains in a window;AnimatedMulticolorGradientViewpauses when detached. - Palette updates snap instantly – pass
animated: true(default) and ensuretransitionSpeedis greater than zero when callingsetColors. - Visible aliasing – increase
renderScaletoward1.0or higher when using heavy noise. - Fewer than expected colors – ensure
repeatsistrueor provide at least as many colors as needed (max eight).
Bug reports and pull requests are welcome on GitHub issues. When contributing:
- Fork the repository and create a feature branch.
- Update or add SwiftUI/Example demos if behaviour changes.
- Run the Example app to verify visual regressions.
- Submit a pull request with a clear summary of the change.
ColorfulX is released under the MIT License. See LICENSE.