Lab 029 – Waiting on a Color Picker
What if a using a color picker was like waiting at baggage claim?
Overview
Just a bit of a fun and distraction while I’m waiting at the airport during a layover.
- Hardcoded array of colors
- Create a sphere for each color and arrange them in a circle
- Position the group of colors in front of the user
- Use a cylinder to clip the visible colors
- Rotate the color group slowly
- Tap gesture to select a color
Video Demo
Full Lab Code
struct Lab029: View {
@State private var colors: [Color] = [
.red,
.pink,
.orange,
.yellow,
.green,
.mint,
.teal,
.cyan,
.blue,
.indigo,
.purple,
.brown,
.white,
Color(uiColor: .systemGray),
Color(uiColor: .systemGray2),
Color(uiColor: .systemGray3),
Color(uiColor: .systemGray4),
Color(uiColor: .systemGray5),
Color(uiColor: .systemGray6),
.black
]
@State var isSpinning: Bool = true
@State var colorGroup = Entity()
@State var selectedColorIndex: Int = 0
var body: some View {
RealityView { content, attachments in
if let scene = try? await Entity(named: "BaggageClaim", in: realityKitContentBundle) {
content.add(scene)
colorGroup.name = "ColorGroup"
colorGroup.position = SIMD3(x: 0, y: 1, z: -5)
scene.addChild(colorGroup)
let radius: Float = 2.5 // 5 meter diameter
let totalColors = Float(colors.count)
for (index, color) in colors.enumerated() {
let colorEntity = ModelEntity(mesh: .generateSphere(radius: 0.25))
colorEntity.name = "Color_\(index)"
colorEntity.components.set(HoverEffectComponent())
colorEntity.components.set(InputTargetComponent())
let collision = CollisionComponent(shapes: [.generateSphere(radius: 0.25)])
colorEntity.components.set(collision)
var material = PhysicallyBasedMaterial()
material.baseColor.tint = .init(color)
material.roughness = 0.5
material.metallic = 0.0
colorEntity.model?.materials = [material]
// Calculate position on circle
let angle = (Float(index) / totalColors) * 2 * .pi
let x = radius * sin(angle)
let z = radius * cos(angle)
colorEntity.position = SIMD3(x: -x, y: 0, z: z)
colorGroup.addChild(colorEntity)
}
if let panel = attachments.entity(for: "Panel") {
panel.position = SIMD3(x: 0, y: 1.5, z: -1.5)
content.add(panel)
}
}
} update: { content, attachments in
// Empty update closure is fine
} attachments: {
Attachment(id: "Panel") {
VStack {
Text("Selected Color")
RoundedRectangle(cornerRadius: 5)
.fill(Color(colors[selectedColorIndex]))
.padding()
}
.frame(width: 300, height: 200)
.glassBackgroundEffect()
}
}
.gesture(tapGesture)
.task {
while true {
try? await Task.sleep(for: .milliseconds(16)) // ~60fps
if isSpinning {
colorGroup.orientation *= simd_quatf(angle: .pi/6300, axis: SIMD3<Float>(0, 1, 0)) // 0.5 degrees per frame
}
}
}
}
var tapGesture: some Gesture {
TapGesture(count: 2)
.targetedToAnyEntity()
.onEnded { value in
let name = value.entity.name
let index = Int(name.split(separator: "_")[1])!
selectedColorIndex = index
}
}
}Support our work so we can continue to bring you new examples and articles.
Download the Xcode project with this and many more labs from Step Into Vision.

Follow Step Into Vision