Skip to content

Instantly share code, notes, and snippets.

@victorBaro
Created August 18, 2025 01:28
Show Gist options
  • Save victorBaro/23b8172c87cc3c2d06801014a14b293c to your computer and use it in GitHub Desktop.
Save victorBaro/23b8172c87cc3c2d06801014a14b293c to your computer and use it in GitHub Desktop.
Playground to debug a metal shader transition
import SwiftUI
struct SlideAwayPlayground: View {
@State private var progress: Float = 0.5
@State private var direction: Float = 1.0
@State private var selectedImage = 0
@State private var showGuides = true
@State private var animateProgress = false
@State private var animationSpeed: Double = 2.0
let images: [ImageResource] = [.abstractUnsplash, .geoUnsplash, .aiavatar]
var body: some View {
VStack(spacing: 20) {
Text("SLIDE-AWAY SHADER PLAYGROUND")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.red)
.padding()
// Main shader view with guides
ZStack {
// Background grid for reference
if showGuides {
Rectangle()
.fill(Color.gray.opacity(0.1))
.overlay(
VStack(spacing: 0) {
ForEach(0..<10) { _ in
HStack(spacing: 0) {
ForEach(0..<10) { _ in
Rectangle()
.stroke(Color.gray.opacity(0.3), lineWidth: 0.5)
.frame(width: 30, height: 30)
}
}
}
}
)
}
// The shader effect
Image(images[selectedImage])
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 300, height: 300)
.clipped()
.visualEffect { content, proxy in
content.distortionEffect(
// Find this metal code in the following post:
// https://nerdyak.tech/development/2023/06/16/distortionEffect-with-Metal-shaders-for-better-transitions.html
ShaderLibrary.slideAwayRipple(
.float2(Float(proxy.size.width), Float(proxy.size.height)),
.float(progress),
.float(direction)
),
maxSampleOffset: CGSize(width: 20, height: 40)
)
}
.border(Color.black, width: 2)
// Progress line indicator
if showGuides {
let linePosition = direction > 0 ?
(CGFloat(progress) * 300) :
(300 - CGFloat(progress) * 300)
Rectangle()
.fill(Color.red)
.frame(width: 2, height: 300)
.offset(x: linePosition - 150)
Text("PROGRESS LINE")
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.red)
.offset(x: linePosition - 150, y: -170)
}
}
.frame(width: 300, height: 300)
// Controls
VStack(spacing: 15) {
// Progress control
VStack {
HStack {
Text("PROGRESS: \(progress, specifier: "%.3f")")
.fontWeight(.bold)
Spacer()
Button(animateProgress ? "STOP" : "ANIMATE") {
animateProgress.toggle()
}
.foregroundColor(animateProgress ? .red : .blue)
.fontWeight(.bold)
}
Slider(value: $progress, in: 0...2) {
Text("Progress")
}
.disabled(animateProgress)
HStack {
Text("0.0 (all hidden)")
.font(.caption)
Spacer()
Text("1.0 (identity)")
.font(.caption)
.fontWeight(.bold)
Spacer()
Text("2.0 (all pushed)")
.font(.caption)
}
}
// Direction control
VStack {
Text("DIRECTION: \(direction > 0 ? "LEFT→RIGHT (+1)" : "RIGHT→LEFT (-1)")")
.fontWeight(.bold)
HStack {
Button("LEFT→RIGHT (+1)") {
direction = 1.0
}
.foregroundColor(direction > 0 ? .white : .blue)
.padding()
.background(direction > 0 ? Color.blue : Color.clear)
.border(Color.blue)
Button("RIGHT→LEFT (-1)") {
direction = -1.0
}
.foregroundColor(direction < 0 ? .white : .red)
.padding()
.background(direction < 0 ? Color.red : Color.clear)
.border(Color.red)
}
}
// Image selection
VStack {
Text("IMAGE SELECTION")
.fontWeight(.bold)
Picker("Image", selection: $selectedImage) {
Text("Abstract").tag(0)
Text("Geometry").tag(1)
Text("AI Avatar").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
}
// Animation speed (when animating)
if animateProgress {
VStack {
Text("ANIMATION SPEED: \(animationSpeed, specifier: "%.1f")s")
.fontWeight(.bold)
Slider(value: $animationSpeed, in: 0.5...5.0) {
Text("Speed")
}
}
}
// Guides toggle
Button(showGuides ? "HIDE GUIDES" : "SHOW GUIDES") {
showGuides.toggle()
}
.foregroundColor(showGuides ? .red : .green)
.fontWeight(.bold)
}
.padding()
}
.onReceive(Timer.publish(every: 0.016, on: .main, in: .common).autoconnect()) { _ in
if animateProgress {
withAnimation(.linear(duration: 0.016)) {
progress += Float(0.016 / animationSpeed * 2) // 0 to 2 over animationSpeed seconds
if progress > 2.0 {
progress = 0.0
}
}
}
}
}
}
#Preview {
SlideAwayPlayground()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment