axiom-scenekit-ref
PassSceneKit → RealityKit concept mapping, complete API cross-reference for migration, scene graph API, materials, lighting, camera, physics, animation, constraints
(0)
460stars
0downloads
1views
Install Skill
Skills are third-party code from public GitHub repositories. SkillHub scans for known malicious patterns but cannot guarantee safety. Review the source code before installing.
Install globally (user-level):
npx skillhub install CharlesWiltgen/Axiom/axiom-scenekit-refInstall in current project:
npx skillhub install CharlesWiltgen/Axiom/axiom-scenekit-ref --projectSuggested path: ~/.claude/skills/axiom-scenekit-ref/
SKILL.md Content
---
name: axiom-scenekit-ref
description: SceneKit → RealityKit concept mapping, complete API cross-reference for migration, scene graph API, materials, lighting, camera, physics, animation, constraints
license: MIT
compatibility: [iOS 8+, macOS 10.8+, tvOS 9+]
metadata:
version: "1.0.0"
---
# SceneKit API Reference & Migration Mapping
Complete API reference for SceneKit with RealityKit equivalents for every major concept.
## When to Use This Reference
Use this reference when:
- Looking up SceneKit → RealityKit API equivalents during migration
- Checking specific SceneKit class properties or methods
- Planning which SceneKit features have direct RealityKit counterparts
- Understanding architectural differences between scene graph and ECS
---
## Part 1: SceneKit → RealityKit Concept Mapping
### Core Architecture
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `SCNScene` | `RealityViewContent` / `Entity` (root) | RealityKit scenes are entity hierarchies |
| `SCNNode` | `Entity` | Lightweight container in both |
| `SCNView` | `RealityView` (SwiftUI) | `ARView` for UIKit on iOS |
| `SceneView` (SwiftUI) | `RealityView` | SceneView deprecated iOS 26 |
| `SCNRenderer` | `RealityRenderer` | Low-level Metal rendering |
| Node properties | Components | ECS separates data from hierarchy |
| `SCNSceneRendererDelegate` | `System` / `SceneEvents.Update` | Frame-level updates |
| `.scn` files | `.usdz` / `.usda` files | Convert with `xcrun scntool` |
### Geometry & Rendering
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `SCNGeometry` | `MeshResource` | RealityKit generates from code or loads USD |
| `SCNBox`, `SCNSphere`, etc. | `MeshResource.generateBox()`, `.generateSphere()` | Similar built-in shapes |
| `SCNMaterial` | `SimpleMaterial`, `PhysicallyBasedMaterial` | PBR-first in RealityKit |
| `SCNMaterial.lightingModel = .physicallyBased` | `PhysicallyBasedMaterial` | Default in RealityKit |
| `SCNMaterial.diffuse` | `PhysicallyBasedMaterial.baseColor` | Different property name |
| `SCNMaterial.metalness` | `PhysicallyBasedMaterial.metallic` | Different property name |
| `SCNMaterial.roughness` | `PhysicallyBasedMaterial.roughness` | Same concept |
| `SCNMaterial.normal` | `PhysicallyBasedMaterial.normal` | Same concept |
| Shader modifiers | `ShaderGraphMaterial` / `CustomMaterial` | No direct port — must rewrite |
| `SCNProgram` (custom shaders) | `CustomMaterial` with Metal functions | Different API surface |
| `SCNGeometrySource` | `MeshResource.Contents` | Low-level mesh data |
### Transforms & Hierarchy
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `node.position` | `entity.position` | Both SCNVector3 / SIMD3<Float> |
| `node.eulerAngles` | `entity.orientation` (quaternion) | RealityKit prefers quaternions |
| `node.scale` | `entity.scale` | Both SIMD3<Float> |
| `node.transform` | `entity.transform` | 4×4 matrix |
| `node.worldTransform` | `entity.transform(relativeTo: nil)` | World-space transform |
| `node.addChildNode(_:)` | `entity.addChild(_:)` | Same hierarchy concept |
| `node.removeFromParentNode()` | `entity.removeFromParent()` | Same concept |
| `node.childNodes` | `entity.children` | Children collection |
| `node.parent` | `entity.parent` | Parent reference |
| `node.childNode(withName:recursively:)` | `entity.findEntity(named:)` | Named lookup |
### Lighting
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `SCNLight` (`.omni`) | `PointLightComponent` | Point light |
| `SCNLight` (`.directional`) | `DirectionalLightComponent` | Sun/directional light |
| `SCNLight` (`.spot`) | `SpotLightComponent` | Cone light |
| `SCNLight` (`.area`) | No direct equivalent | Use multiple point lights |
| `SCNLight` (`.ambient`) | `EnvironmentResource` (IBL) | Image-based lighting preferred |
| `SCNLight` (`.probe`) | `EnvironmentResource` | Environment probes |
| `SCNLight` (`.IES`) | No direct equivalent | Use light intensity profiles |
### Camera
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `SCNCamera` | `PerspectiveCamera` entity | Entity with camera component |
| `camera.fieldOfView` | `PerspectiveCameraComponent.fieldOfViewInDegrees` | Same concept |
| `camera.zNear` / `camera.zFar` | `PerspectiveCameraComponent.near` / `.far` | Clipping planes |
| `camera.wantsDepthOfField` | Post-processing effects | Different mechanism |
| `camera.motionBlurIntensity` | Post-processing effects | Different mechanism |
| `allowsCameraControl` | Custom gesture handling | No built-in orbit camera |
### Physics
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `SCNPhysicsBody` | `PhysicsBodyComponent` | Component-based |
| `.dynamic` | `.dynamic` | Same mode |
| `.static` | `.static` | Same mode |
| `.kinematic` | `.kinematic` | Same mode |
| `SCNPhysicsShape` | `CollisionComponent` / `ShapeResource` | Separate from body in RealityKit |
| `categoryBitMask` | `CollisionGroup` | Named groups vs raw bitmasks |
| `collisionBitMask` | `CollisionFilter` | Filter-based |
| `contactTestBitMask` | `CollisionEvents.Began` subscription | Event-based contacts |
| `SCNPhysicsContactDelegate` | `scene.subscribe(to: CollisionEvents.Began.self)` | Combine-style events |
| `SCNPhysicsField` | `PhysicsBodyComponent` forces | Apply forces directly |
| `SCNPhysicsJoint` | `PhysicsJoint` | Similar joint types |
### Animation
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `SCNAction` | `entity.move(to:relativeTo:duration:)` | Transform animation |
| `SCNAction.sequence` | Animation chaining | Less declarative in RealityKit |
| `SCNAction.group` | Parallel animations | Apply to different entities |
| `SCNAction.repeatForever` | `AnimationPlaybackController` repeat | Different API |
| `SCNTransaction` (implicit) | No direct equivalent | Explicit animations only |
| `CAAnimation` bridge | `entity.playAnimation()` | Load from USD |
| `SCNAnimationPlayer` | `AnimationPlaybackController` | Playback control |
| Morph targets | Blend shapes in USD | Load via USD files |
### Interaction
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `hitTest(_:options:)` | `RealityViewContent.entities(at:)` | Different API |
| Gesture recognizers on SCNView | `ManipulationComponent` | Built-in drag/rotate/scale |
| `allowsCameraControl` | Custom implementation | No built-in orbit |
### AR Integration
| SceneKit | RealityKit | Notes |
|----------|-----------|-------|
| `ARSCNView` | `RealityView` + `AnchorEntity` | Legacy → modern |
| `ARSCNViewDelegate` | `AnchorEntity` auto-tracking | Event-driven |
| `renderer(_:didAdd:for:)` | `AnchorEntity(.plane)` | Declarative anchoring |
| `ARWorldTrackingConfiguration` | `SpatialTrackingSession` | iOS 18+ |
---
## Part 2: Scene Graph API
### SCNScene
```swift
// Loading
let scene = SCNScene(named: "scene.usdz")!
let scene = try SCNScene(url: url, options: [
.checkConsistency: true,
.convertToYUp: true
])
// Properties
scene.rootNode // Root of node hierarchy
scene.background.contents // Skybox (UIImage, UIColor, MDLSkyCubeTexture)
scene.lightingEnvironment.contents // IBL environment map
scene.fogStartDistance // Fog near
scene.fogEndDistance // Fog far
scene.fogColor // Fog color
scene.isPaused // Pause simulation
```
### SCNNode
```swift
// Creation
let node = SCNNode()
let node = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
// Transform
node.position = SCNVector3(x, y, z)
node.eulerAngles = SCNVector3(pitch, yaw, roll)
node.scale = SCNVector3(1, 1, 1)
node.simdPosition = SIMD3<Float>(x, y, z) // SIMD variants available
node.pivot = SCNMatrix4MakeTranslation(0, -0.5, 0) // Offset pivot point
// Visibility
node.isHidden = false
node.opacity = 1.0
node.castsShadow = true
node.renderingOrder = 0 // Lower = rendered first
// Hierarchy
node.addChildNode(child)
node.removeFromParentNode()
node.childNodes
node.childNode(withName: "name", recursively: true)
node.enumerateChildNodes { child, stop in }
```
---
## Part 3: Materials
### Lighting Models
| Model | Description | Use Case |
|-------|-------------|----------|
| `.physicallyBased` | PBR metallic-roughness | Realistic rendering (recommended) |
| `.blinn` | Blinn-Phong specular | Simple shiny surfaces |
| `.phong` | Phong specular | Classic specular highlight |
| `.lambert` | Diffuse only, no specular | Matte surfaces |
| `.constant` | Unlit, flat color | UI elements, debug visualization |
| `.shadowOnly` | Invisible, receives shadows | AR ground plane |
### Material Properties
```swift
let mat = SCNMaterial()
mat.lightingModel = .physicallyBased
// Textures or scalar values
mat.diffuse.contents = UIImage(named: "albedo") // Base color
mat.metalness.contents = 0.0 // 0 = dielectric, 1 = metal
mat.roughness.contents = 0.5 // 0 = mirror, 1 = rough
mat.normal.contents = UIImage(named: "normal") // Normal map
mat.ambientOcclusion.contents = UIImage(named: "ao") // AO map
mat.emission.contents = UIColor.blue // Glow
mat.displacement.contents = UIImage(named: "height") // Height map
// Options
mat.isDoubleSided = false // Render both sides
mat.writesToDepthBuffer = true
mat.readsFromDepthBuffer = true
mat.blendMode = .alpha // .add, .subtract, .multiply, .screen
mat.transparencyMode = .aOne // .rgbZero for pre-multiplied alpha
```
---
## Part 4: Physics
### Body Types and Properties
```swift
// Dynamic body with custom shape
let shape = SCNPhysicsShape(geometry: SCNSphere(radius: 0.5), options: nil)
let body = SCNPhysicsBody(type: .dynamic, shape: shape)
body.mass = 1.0
body.friction = 0.5
body.restitution = 0.3 // Bounciness
body.damping = 0.1 // Linear damping
body.angularDamping = 0.1 // Angular damping
body.isAffectedByGravity = true
body.allowsResting = true // Sleep optimization
node.physicsBody = body
// Compound shapes
let compound = SCNPhysicsShape(shapes: [shape1, shape2],
transforms: [transform1, transform2])
// Concave (static only)
let concave = SCNPhysicsShape(geometry: mesh, options: [
.type: SCNPhysicsShape.ShapeType.concavePolyhedron
])
```
### Joint Types
| Joint | Description |
|-------|-------------|
| `SCNPhysicsHingeJoint` | Single-axis rotation (door) |
| `SCNPhysicsBallSocketJoint` | Free rotation around point (pendulum) |
| `SCNPhysicsSliderJoint` | Linear movement along axis (drawer) |
| `SCNPhysicsConeTwistJoint` | Limited rotation (ragdoll limb) |
---
## Part 5: Animation API
### SCNAction Catalog
| Category | Actions |
|----------|---------|
| Movement | `move(by:duration:)`, `move(to:duration:)` |
| Rotation | `rotate(by:around:duration:)`, `rotateTo(x:y:z:duration:)` |
| Scale | `scale(by:duration:)`, `scale(to:duration:)` |
| Fade | `fadeIn(duration:)`, `fadeOut(duration:)`, `fadeOpacity(to:duration:)` |
| Visibility | `hide()`, `unhide()` |
| Audio | `playAudio(source:waitForCompletion:)` |
| Custom | `run { node in }`, `customAction(duration:action:)` |
| Composition | `sequence([])`, `group([])`, `repeat(_:count:)`, `repeatForever(_:)` |
| Control | `wait(duration:)`, `removeFromParentNode()` |
### Timing Functions
```swift
action.timingMode = .linear // Default
action.timingMode = .easeIn // Slow start
action.timingMode = .easeOut // Slow end
action.timingMode = .easeInEaseOut // Slow start and end
action.timingFunction = { t in // Custom curve
return t * t // Quadratic ease-in
}
```
---
## Part 6: Constraints
| Constraint | Purpose |
|------------|---------|
| `SCNLookAtConstraint` | Node always faces target |
| `SCNBillboardConstraint` | Node always faces camera |
| `SCNDistanceConstraint` | Maintains min/max distance |
| `SCNReplicatorConstraint` | Copies transform of target |
| `SCNAccelerationConstraint` | Smooths transform changes |
| `SCNSliderConstraint` | Locks to axis |
| `SCNIKConstraint` | Inverse kinematics chain |
```swift
let lookAt = SCNLookAtConstraint(target: targetNode)
lookAt.isGimbalLockEnabled = true // Prevent roll
lookAt.influenceFactor = 0.8 // Partial constraint
node.constraints = [lookAt]
```
**In RealityKit**: No direct constraint system. Implement with `System` update logic or `entity.look(at:from:relativeTo:)`.
---
## Part 7: Scene Configuration
### SCNView Configuration
| Property | Default | Description |
|----------|---------|-------------|
| `antialiasingMode` | `.multisampling4X` | MSAA level |
| `preferredFramesPerSecond` | 60 | Target frame rate |
| `allowsCameraControl` | `false` | Built-in orbit/pan/zoom |
| `autoenablesDefaultLighting` | `false` | Add default light if none |
| `showsStatistics` | `false` | FPS/node/draw count overlay |
| `isTemporalAntialiasingEnabled` | `false` | TAA smoothing |
| `isJitteringEnabled` | `false` | Temporal jitter for TAA |
| `debugOptions` | `[]` | `.showPhysicsShapes`, `.showBoundingBoxes`, `.renderAsWireframe` |
---
## Resources
**WWDC**: 2014-609, 2014-610, 2017-604, 2019-612
**Docs**: /scenekit, /scenekit/scnscene, /scenekit/scnnode, /scenekit/scnmaterial, /scenekit/scnphysicsbody, /scenekit/scnaction
**Skills**: axiom-scenekit, axiom-realitykit, axiom-realitykit-ref