A Swift package for composable Metal shaders and holographic UI effects in SwiftUI.
butterflyShader.mov
Create holographic cards with composable shader effects:
import ShaderKit
HolographicCardContainer(width: 260, height: 380) {
CardContent()
.foil()
.glitter()
.lightSweep()
}The HolographicCardContainer provides:
- Device motion tracking via gyroscope
- Drag gesture for manual tilt control
- 3D rotation effects synchronized with tilt
- Dynamic shadow based on tilt angle
- Automatic shader context injection
Add ShaderKit to your project via Xcode:
- File → Add Package Dependencies
- Enter the repository URL
- Select your version requirements
Or add to your Package.swift:
dependencies: [
.package(url: "https://github.com/jamesrochabrun/ShaderKit.git", from: "1.0.0")
]The package ships three library products — depend only on what you need:
- ShaderKit — the composable Metal shader primitives and holographic containers
- ShaderKitUI — ready-made interactive components (jelly switch, jelly button)
- ShaderCards — Pokémon-style holographic trading cards built on ShaderKit (see ShaderCards)
The library (added via SPM, above) and the agent skill are two separate things. Coding agents do not scan Swift Package checkouts for skills, so adding ShaderKit as a dependency will never install or activate the skill — there is nothing to "also install the skill" as part of adding the package.
The skill is a Claude Code plugin, installed independently of your SPM setup (once per machine):
/plugin marketplace add jamesrochabrun/ShaderKit /plugin install shaderkit@shaderkitThen it's available as
shaderkit:trading-card. Full details in Agent skill: trading-card.
ShaderKit provides 52 composable shader effects, including holographic, glass, metallic, material, seasonal, paper, and interactive 3D treatments.
| Effect | Description | Parameters |
|---|---|---|
.foil() |
Rainbow foil overlay | intensity: Double = 1.0 |
.invertedFoil() |
Inverted foil with shine | intensity: Double = 0.7 |
.maskedFoil() |
Foil with masked area | imageWindow: SIMD4<Float>, intensity: Double = 1.0 |
.foilTexture() |
Fine diagonal line texture | imageWindow: SIMD4<Float> |
| Effect | Description | Parameters |
|---|---|---|
.glitter() |
Sparkle particle overlay | density: Double = 50 |
.multiGlitter() |
Multi-scale sparkle particles | density: Double = 80 |
.sparkles() |
Tilt-activated sparkle grid | - |
.maskedSparkle() |
Sparkles in masked area only | imageWindow: SIMD4<Float> |
.rainbowGlitter() |
Rainbow with luminosity blend | intensity: Double = 0.7 |
.shimmer() |
Metallic shimmer effect | intensity: Double = 0.7 |
| Effect | Description | Parameters |
|---|---|---|
.lightSweep() |
Sweeping light band | - |
.radialSweep() |
Rotating radial light sweep | - |
.angledSweep() |
Angled light sweep | - |
.glare() |
Following light hotspot | intensity: Double = 1.0 |
.simpleGlare() |
Simple radial glare | intensity: Double = 0.7 |
.edgeShine() |
Edge highlight effect | - |
| Effect | Description | Parameters |
|---|---|---|
.diamondGrid() |
Diamond grid pattern | intensity: Double = 1.0 |
.intenseBling() |
Maximum intensity diamond holo | - |
.starburst() |
Radial rainbow rays from center | intensity: Double = 1.0 |
.blendedHolo() |
Luminance-blended rainbow | intensity: Float = 0.7, saturation: Float = 0.75 |
.verticalBeams() |
Vertical rainbow beam pattern | intensity: Double = 0.7 |
.diagonalHolo() |
Diagonal lines with 3D depth | intensity: Double = 0.7 |
.crisscrossHolo() |
Criss-cross diamond pattern | intensity: Double = 0.7 |
.galaxyHolo() |
Galaxy/cosmos with rainbow overlay | intensity: Double = 0.7 |
.radialStar() |
Star pattern with radial fade | intensity: Double = 0.7 |
.subtleGradient() |
Large-scale subtle gradient | intensity: Double = 0.7 |
.metallicCrosshatch() |
Metallic sun-pillar with crosshatch | intensity: Double = 0.7 |
.spiralRings() |
Concentric spiral rings with metallic effect | intensity: Double = 0.8, ringCount: Double = 20, spiralTwist: Double = 0.5 |
| Effect | Description | Parameters |
|---|---|---|
.glassEnclosure() |
Plastic/glass layer with beveled edges | intensity: Double = 1.0, cornerRadius: Double = 0.05, bevelSize: Double = 0.7, glossiness: Double = 0.8 |
.glassSheen() |
Simple glass sheen overlay | intensity: Double = 0.7, spread: Double = 0.5 |
.glassBevel() |
Edge bevel with visual thickness | intensity: Double = 0.8, thickness: Double = 0.6 |
.chromaticGlass() |
Prismatic RGB separation at edges | intensity: Double = 0.6, separation: Double = 0.4 |
| Effect | Description | Parameters |
|---|---|---|
.snowfall() |
Falling snowflakes with twinkling stars | intensity: Double = 0.8, snowDensity: Double = 0.5, starDensity: Double = 0.6, primaryColor: SIMD4<Float>, secondaryColor: SIMD4<Float> |
.frozen() |
Icy silver shimmer with floating blue stars | intensity: Double = 0.85, starDensity: Double = 0.6, shimmerIntensity: Double = 0.8, iceColor: SIMD4<Float>, starColor: SIMD4<Float> |
| Effect | Description | Parameters |
|---|---|---|
.polishedAluminum() |
Polished aluminum with diagonal rainbow reflection | intensity: Double = 0.85 |
These opaque-style coatings preserve the source layer's luminance and contrast, keeping artwork and typography readable through the material.
| Effect | Description | Parameters |
|---|---|---|
.brushedTitanium() |
Satin titanium with directional brushing | intensity: Double = 0.85 |
.blackChrome() |
Mirror-black chrome with spectral edges | intensity: Double = 0.88 |
.roseGold() |
Warm brushed rose-gold plating | intensity: Double = 0.82 |
.liquidMercury() |
Flowing mirror-silver folds | intensity: Double = 0.84 |
.anodizedTitanium() |
Angle-reactive heat oxidation colors | intensity: Double = 0.84 |
.damascusSteel() |
Wavy, layered forged-steel grain | intensity: Double = 0.86 |
.forgedCarbon() |
Chopped carbon with reactive flakes | intensity: Double = 0.88 |
.copperPatina() |
Copper with procedural turquoise verdigris | intensity: Double = 0.82 |
.pearlCeramic() |
Opaque ceramic with pearlescent glaze | intensity: Double = 0.80 |
.oilSlick() |
Dark chrome with thin-film rainbow bands | intensity: Double = 0.84 |
Use .tradingCardHolo(_:intensity:) with any TradingCardHoloStyle. The
catalog contains 21 procedural constructions: Regular, Cosmos, Reverse,
Radiant, Amazing, V, VMAX, VSTAR, Full Art, Rainbow, Rainbow Alternate,
Gold Secret, Shiny, Shiny V, Shiny VMAX, five Trainer Gallery variants, and
the special rainbow-secret treatment.
Each style also exposes referenceSheetName for traceable QA and migration
tooling (for example, .vMax.referenceSheetName == "v-max.css").
CardContent()
.tradingCardHolo(.vMax, intensity: 0.88)| Effect | Description | Parameters |
|---|---|---|
.water() |
Water caustic effect inspired by https://shaders.paper.design/water | colorBack: SIMD4<Float>, colorHighlight: SIMD4<Float>, highlights: Double = 0.07, edges: Double = 0.8, waves: Double = 0.3, caustic: Double = 0.1, size: Double = 1.0, speed: Double = 1.0, scale: Double = 0.8 |
| Effect | Description | Parameters |
|---|---|---|
.liquidTech() |
Experimental liquid tech highlights | intensity: Double = 0.9, speed: Double = 1.0, scale: Double = 1.0 |
Chain multiple effects to create unique combinations:
// Holographic trading card
HolographicCardContainer(width: 260, height: 380) {
CardContent()
.foil()
.glitter()
.lightSweep()
}
// Premium gold card with starburst
HolographicCardContainer(
width: 280,
height: 400,
shadowColor: .yellow,
rotationMultiplier: 12
) {
CardContent()
.starburst()
.radialSweep()
.multiGlitter()
}
// Psychic holographic effect
HolographicCardContainer(width: 280, height: 400, shadowColor: .purple) {
CardContent()
.foil()
.glitter()
.lightSweep()
}
// Layered effect with blended holo
HolographicCardContainer(width: 260, height: 364, shadowColor: .yellow) {
ZStack {
BackgroundLayer()
.blendedHolo(intensity: 0.7, saturation: 0.75)
SparkleLayer()
.sparkles()
ArtworkLayer()
}
.angledSweep()
}
// Glass-encased collectible card
HolographicCardContainer(width: 280, height: 400, shadowColor: .white.opacity(0.3)) {
CardContent()
.foil()
.glitter()
.glassEnclosure()
}
// Winter snowfall effect
HolographicCardContainer(width: 260, height: 380, shadowColor: .cyan) {
CardContent()
.snowfall(
primaryColor: SIMD4<Float>(0.3, 0.5, 0.7, 1.0),
secondaryColor: SIMD4<Float>(0.2, 0.4, 0.6, 1.0)
)
}
// Frozen ice magic effect
HolographicCardContainer(width: 260, height: 380, shadowColor: .cyan) {
CardContent()
.frozen(starDensity: 0.7, shimmerIntensity: 0.9)
}
// Polished aluminum holographic card
HolographicCardContainer(width: 260, height: 380, shadowColor: .gray) {
CardContent()
.polishedAluminum()
}
// Opaque premium material, optionally finished with a glass clear coat
HolographicCardContainer(width: 260, height: 380, shadowColor: .gray) {
CardContent()
.brushedTitanium()
.glassSheen(intensity: 0.16, spread: 0.7)
}
// Absolute pointer tracking for physical trading-card interaction
HolographicCardContainer(
width: 260,
height: 380,
rotationMultiplier: 14.3,
interactionMode: .surfacePointer
) {
CardContent()
.tradingCardHolo(.vMax)
}For custom tilt control without HolographicCardContainer:
CardContent()
.shaderContext(tilt: myTiltPoint, time: elapsedTime)
.shader(.foil(intensity: 0.8))
.shader(.glitter(density: 75))
.shader(.lightSweep)Apply effects using the generic .shader() modifier:
CardContent()
.shaderContext(tilt: tilt, time: time)
.shader(.foil(intensity: 0.8))
.shader(.glitter())
.shader(.lightSweep)Apply effects only to specific areas using UV coordinates:
// Define image window bounds (UV space 0-1)
let imageWindow = SIMD4<Float>(
0.04, // minX
0.11, // minY
0.96, // maxX
0.55 // maxY
)
CardContent()
.maskedFoil(imageWindow: imageWindow)
.maskedSparkle(imageWindow: imageWindow)
.foilTexture(imageWindow: imageWindow)ShaderKitUI provides ready-to-use interactive UI components built with Metal shaders.
A 3D jelly toggle switch with spring physics and sound effects. Inspired by TypeGPU's Jelly Switch.
import ShaderKitUI
struct ContentView: View {
@State private var isOn = false
var body: some View {
JellySwitch(isOn: $isOn)
.ignoresSafeArea()
}
}Customization options:
JellySwitch(
isOn: $isOn,
jellyColor: .blue, // Custom jelly color
darkMode: true, // Dark ambient lighting
soundEnabled: false // Disable toggle sounds
)A 3D jelly button with spring physics that squishes on tap and jiggles on long press.
import ShaderKitUI
struct ContentView: View {
var body: some View {
JellyButton {
print("Tapped!")
}
.ignoresSafeArea()
}
}Customization options:
JellyButton(
action: { doSomething() },
jellyColor: .blue, // Custom jelly color
darkMode: true, // Dark ambient lighting
soundEnabled: false // Disable press/release sounds
)A complete holographic trading-card library in the spirit of Pokémon TCG, built on ShaderKit's shaders. Every card is rendered entirely in SwiftUI — procedural artwork (paths, gradients, Canvas), real-time Metal foil, and zero image assets.
- 95-card preset library — 33 original creatures across 11 elements, trainers, energies, 22 special editions, and 21 CSS-parity holo showcases
- 13 rarity tiers with faithful foil recipes — from matte commons to reverse holo, radiant, rainbow rare, and gold hyper rare
- Complete modern holo catalog — all 21 active treatments from the Pokémon Cards CSS reference, plus ten premium materials (brushed titanium, black chrome, Damascus steel, oil slick…)
- Your own images as card art — hand
CardBuildera photo and it gets the full frame, foil, and explode treatment - Explodable composition — every card separates into frame, artwork, chrome, and one 3D layer per shader pass
- Card Studio — a live card builder with editable shader stacks, rarity presets, and photo import
Add the ShaderCards product to your target, then:
import ShaderCards
// An interactive holographic card from the library — drag to tilt,
// foil and sparkles react in real time.
TradingCardView(.creature(CardLibrary.emberfox), width: 300)
// The whole library in a browsable grid.
NavigationStack { CardGalleryView() }
// A static face for lists and thumbnails (no shader passes).
CardFaceView(card: .creature(CardLibrary.tidecaller), width: 160)CardBuilder is a fluent API — every stat is optional:
let card = CardBuilder(name: "Solarix", element: .fire)
.hp(160)
.stage(.ex)
.rarity(.hyperRare)
.artwork(.pyroclaw)
.ability("Solar Core", text: "Once during your turn, heal 30 damage from this card.")
.attack("Nova Burst", cost: [.fire, .fire], damage: "150")
.weakness(.water)
.flavor("Forged in the heart of a dying star.")
.build()
TradingCardView(card)Use any image as the artwork — it gets the full frame and foil treatment:
let photoCard = CardBuilder(name: "Photon", element: .psychic)
.rarity(.holoRare)
.artwork(imageData: myImageData) // PNG / JPEG / HEIC
.build()Pick the face layout independently of rarity with .layout(_:):
.framed— classic beveled art window with the stats body.fullArt— edge-to-edge artwork, stats float on scrims.minimal— poster style: just the name banner, element mark, and info linenil(default) — follows the rarity: ultra rare and above go full-art
| Rarity | Treatment |
|---|---|
| Common / Uncommon | Matte |
| Rare | Subtle glass sheen |
| Holo Rare | Vertical rainbow beams in the art window + sparkles |
| Reverse Holo | Foil on the frame, matte art window |
| Double Rare (ex) | Diagonal holo art + glitter |
| Ultra Rare (full art) | Full-bleed art, etched foil + luminance-blended holo + light sweep |
| Illustration Rare | Full-bleed art, galaxy holo + glitter |
| Special Illustration Rare | Full-bleed art, criss-cross holo + multi-glitter |
| Rainbow Rare | Pastel rainbow glitter over the whole card |
| Hyper Rare | Gold-plated art, gold frame, metallic crosshatch + glitter |
| Radiant | Criss-cross lattice in the art window |
| Amazing Rare | Galaxy swirl + multi-glitter art window |
Every recipe lives in CardFinish.finish(for:) — override any card's finish via TradingCardView(card, finish: customFinish).
CardLibrary.allCards // all 95
CardLibrary.showcase // one spectacular card per holo treatment
CardLibrary.specialEditionCards // 22 foil and material designs
CardLibrary.premiumMaterialCards // the 10 opaque material cards
CardLibrary.cssParityCards // all 21 CSS-reference treatments
CardLibrary.cards(for: .fire) // by element
CardLibrary.cards(of: .rainbowRare) // by rarityApply a special finish to any card:
CardBuilder(name: "Emberfox", element: .fire)
.artwork(.emberfox)
.finish(.goldenSweep) // or .codex, .starburstGold, .frozenCrystal…
.layout(.minimal)
.build()The ten opaque premium materials are available directly as .brushedTitanium, .blackChrome, .roseGold, .liquidMercury, .anodizedTitanium, .damascusSteel, .forgedCarbon, .copperPatina, .pearlCeramic, and .oilSlick. Complete holo constructions apply via .finish(.tradingCardHolo(.vMax)) — see the CSS parity matrix for the full reference-sheet mapping.
ExplodableCardView decomposes a card into discrete layers — frame base, artwork, chrome, and one layer per shader pass — presented with ShaderKit's CardLayerExplodeContainer. Drag to orbit (angular drags rotate in Z), pinch to spread the layers, and it springs into an isometric explode on appear:
ExplodableCardView(card: .creature(CardLibrary.mindmoth), showsTransformControls: true)An interactive builder with rarity presets, editable foil stacks for the art window and the whole card, photo import, transform controls, and a live preview that switches between the holographic card and its exploded layers:
NavigationStack { CardStudioView() }Open the package in Xcode and run the ShaderCardsDemo scheme (macOS or iOS) to browse the full library, foil showcase, special editions, the premium-material collection, and Card Studio.
The repo doubles as an agent plugin bundling one skill:
- trading-card — turn an image into a holographic Pokémon-style trading card built from ShaderKit primitives (
HolographicCardContainer+ a hand-tuned shader stack + glass info panels), like the cards in the demo app. Invoke/trading-card <image-path>— no interview. Needs only theShaderKitproduct.
It installs through the standard plugin mechanisms — nothing writes into your project.
/plugin marketplace add jamesrochabrun/ShaderKit
/plugin install shaderkit@shaderkit
Get skill updates later with /plugin marketplace update. To try the skill while working inside this repo, run claude --plugin-dir . instead.
Codex: register the marketplace with codex plugin marketplace add jamesrochabrun/ShaderKit, then install ShaderKit from the /plugins browser (or the ChatGPT desktop app's Plugins pane).
Adding the Swift package dependency alone does not activate the skill — agents don't scan package checkouts, so install the plugin once per machine.
Once installed, run /trading-card <image-path> (optionally add a short hint like "icy" or "gold legendary" to steer the look). No interview — the skill:
- Reads your image and invents a cool Pokémon-style creature name and stats (HP, role/title, a motto, two signature attacks with damage numbers).
- Randomly picks a holographic design — or maps your hint to the closest vibe (Winter Frost, Psychic Cosmic, Burst Hero, Secret Rare, …), which fixes the palette, effect stack, and glow together.
- Composes the card from ShaderKit primitives: your subject over a shimmering foil background with translucent "glass" info panels.
- Renders a single SwiftUI showcase view — tilt-interactive with live Metal effects, centered on a dark stage.
- Offers quick variations — reroll the design, swap the pattern effect, or tweak the stats.
Metal toolchain: ShaderKit foils are Metal shaders. On Xcode 26+ the Metal Toolchain is a separate download; if a build reports
missing Metal Toolchain, runxcodebuild -downloadComponent MetalToolchainonce.
- iOS 17.0+ / macOS 14.0+
- Swift 5.9+
- Xcode 15+
Note — Metal shaders in Swift packages are compiled by Xcode's build system. Build through Xcode (or
xcodebuild); plainswift buildproduces cards without foil.
MIT License


