# Asreonn Godot Superpowers Godot Sync Parallax

> Sync Parallax Layer Positions

- Skill: `tomevault-io/asreonn-godot-superpowers-godot-sync-parallax` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/asreonn-godot-superpowers-godot-sync-parallax`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/asreonn-godot-superpowers-godot-sync-parallax/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/asreonn-godot-superpowers-godot-sync-parallax

---


# Sync Parallax Layer Positions

## Core Principle

**Parallax layers need special math for correct editor preview.** Motion scale affects runtime position.

## What This Skill Does

Parallax systems in Godot are complex:
```gdscript
# Godot's parallax formula:
final_position = layer_position - (camera_position * motion_scale)
```

**The problem:**
- Editor shows layer at `layer_position`
- Runtime shows layer at `layer_position - (camera_position * motion_scale)`
- Editor preview doesn't match gameplay

**The solution:**
Calculate correct `layer_position` so runtime appearance matches intended design.

## Example Problem

```ini
# parallax_background.tscn
[node name="ParallaxBackground" type="ParallaxBackground"]

[node name="SkyLayer" type="ParallaxLayer" parent="."]
motion_scale = Vector2(0.2, 0.2)  # Slow parallax
position = Vector2(0, 0)

# Camera starts at (640, 360)
# Runtime position: 0 - (640 * 0.2) = -128 (WRONG!)
# Sky layer appears shifted left in game
```

**After sync:**
```ini
[node name="SkyLayer" type="ParallaxLayer" parent="."]
motion_scale = Vector2(0.2, 0.2)
position = Vector2(128, 72)  # Corrected!

# Runtime position: 128 - (640 * 0.2) = 0 (CORRECT!)
# Sky layer appears centered in game, matching editor
```

## Detection Patterns

Identifies parallax systems:

### ParallaxBackground Structure
```ini
[node name="ParallaxBackground" type="ParallaxBackground"]
scroll_offset = Vector2(0, 0)
scroll_base_scale = Vector2(1, 1)

[node name="Layer1" type="ParallaxLayer" parent="."]
motion_scale = Vector2(0.5, 0.5)
motion_offset = Vector2(0, 0)
```

### Multiple Layers with Different Scales
```ini
[node name="SkyLayer" parent="ParallaxBackground"]
motion_scale = Vector2(0.1, 0.1)  # Very slow (far away)

[node name="MountainsLayer" parent="ParallaxBackground"]
motion_scale = Vector2(0.3, 0.3)  # Slow (middle distance)

[node name="TreesLayer" parent="ParallaxBackground"]
motion_scale = Vector2(0.7, 0.7)  # Fast (close)
```

## When to Use

### Designing Parallax Backgrounds
Creating multi-layer parallax effect in editor.

### Debugging Parallax Issues
Layers appear at wrong positions in game.

### Visual Level Design
Want WYSIWYG for parallax systems.

### Multiple Parallax Layers
Complex parallax with many layers at different scales.

## Process

1. **Scan** - Find ParallaxBackground and ParallaxLayer nodes
2. **Detect Camera** - Find camera start position
3. **Calculate** - Apply inverse parallax formula
4. **Update Positions** - Correct layer positions in .tscn
5. **Update Offsets** - Adjust motion_offset if needed
6. **Validate** - Ensure layers align correctly
7. **Commit** - Git commit per parallax system

## Parallax Math

### Forward Formula (Godot's runtime calculation)
```
displayed_position = layer_position - (camera_position * motion_scale)
```

### Inverse Formula (What we need for editor)
```
layer_position = desired_position + (camera_position * motion_scale)
```

### Example Calculation

**Want sky layer centered at (0, 0) in game:**
- Camera starts at: (640, 360)
- Motion scale: (0.2, 0.2)
- Calculate: layer_position = 0 + (640 * 0.2) = 128
- Calculate: layer_position = 0 + (360 * 0.2) = 72
- **Set layer position in editor: (128, 72)**

## Example Transformations

### Example 1: Sky Layer

**Before (Broken):**
```ini
[node name="SkyLayer" type="ParallaxLayer"]
motion_scale = Vector2(0.2, 0.2)
position = Vector2(0, 0)
# Sprite at (0, 0) relative to layer

# Camera at (640, 360)
# Runtime: 0 - (640 * 0.2) = -128
# Sky appears -128 pixels left (WRONG)
```

**After (Fixed):**
```ini
[node name="SkyLayer" type="ParallaxLayer"]
motion_scale = Vector2(0.2, 0.2)
position = Vector2(128, 72)  # Corrected
# Sprite at (0, 0) relative to layer

# Runtime: 128 - (640 * 0.2) = 0
# Sky appears centered (CORRECT)
```

### Example 2: Multiple Layers

**Before (All at origin):**
```ini
[node name="Sky" type="ParallaxLayer"]
motion_scale = Vector2(0.1, 0.1)
position = Vector2(0, 0)

[node name="Mountains" type="ParallaxLayer"]
motion_scale = Vector2(0.3, 0.3)
position = Vector2(0, 0)

[node name="Trees" type="ParallaxLayer"]
motion_scale = Vector2(0.7, 0.7)
position = Vector2(0, 0)

# All layers shifted left at runtime
```

**After (Corrected):**
```ini
[node name="Sky" type="ParallaxLayer"]
motion_scale = Vector2(0.1, 0.1)
position = Vector2(64, 36)  # 640*0.1, 360*0.1

[node name="Mountains" type="ParallaxLayer"]
motion_scale = Vector2(0.3, 0.3)
position = Vector2(192, 108)  # 640*0.3, 360*0.3

[node name="Trees" type="ParallaxLayer"]
motion_scale = Vector2(0.7, 0.7)
position = Vector2(448, 252)  # 640*0.7, 360*0.7

# All layers centered correctly at runtime
```

## Advanced: Motion Offset

### Motion Offset vs Position
- **position**: Layer's base position
- **motion_offset**: Additional offset applied with motion_scale

Combined formula:
```
displayed_position = (layer_position + motion_offset) - (camera_position * motion_scale)
```

Syncing considers both properties.

## Smart Detection

**Identifies parallax systems:**
- ParallaxBackground node present
- ParallaxLayer children with motion_scale ≠ (1, 1)
- Sprite/TextureRect children of layers

**Detects camera:**
1. Camera2D in scene hierarchy
2. Camera position from parent scene
3. Viewport center (default: 640, 360 for 1280x720)

**Calculates corrections:**
- Per-layer based on motion_scale
- Accounts for motion_offset
- Handles ParallaxBackground scroll_offset

## What Gets Created

- Updated .tscn files with corrected layer positions
- Comments documenting parallax calculations
- Validation ensuring layers align correctly
- Git commits per parallax system

## Integration

Works with:
- **godot-sync-camera-positions** - Non-parallax camera following
- **godot-sync-static-positions** - Static position conflicts
- **godot-fix-positions** (orchestrator) - All position sync operations

## Safety

- Parallax behavior unchanged at runtime
- Only editor positions updated
- Original .tscn preserved in git
- Validation ensures layers align
- Rollback on validation failure

## When NOT to Use

Don't sync if:
- Parallax positions intentionally offset
- Custom parallax implementation (not ParallaxBackground)
- Dynamic parallax scaling at runtime
- Camera position highly variable

## Benefits

- **Visual Design** - Design parallax layers in editor
- **WYSIWYG** - Preview matches gameplay
- **Debugging** - Obvious when layers misaligned
- **Faster Iteration** - See results immediately
- **Team Friendly** - Level designers work in editor

## Common Parallax Patterns

### Infinite Scrolling Background
```ini
[node name="CloudsLayer" type="ParallaxLayer"]
motion_scale = Vector2(0.2, 0)  # Horizontal only
motion_mirroring = Vector2(1280, 0)  # Repeat horizontally
```

Sync: Correct horizontal position only.

### Vertical Parallax (Platformer)
```ini
[node name="SkyLayer" type="ParallaxLayer"]
motion_scale = Vector2(0, 0.1)  # Vertical only
```

Sync: Correct vertical position only.

### Full 2D Parallax
```ini
[node name="BackgroundLayer" type="ParallaxLayer"]
motion_scale = Vector2(0.3, 0.3)  # Both axes
```

Sync: Correct both x and y positions.

## Validation

After syncing, validates:
- All layers visible in editor
- Layer order preserved (back to front)
- Sprites aligned as intended
- No layer at negative positions (unless intentional)
- Editor preview looks realistic

## Camera Position Detection

### Priority Order
1. Camera2D in current scene → use position
2. Camera2D in parent scene → use position
3. Viewport size → assume center (width/2, height/2)
4. Project settings → get viewport size
5. Ask user if uncertain

## Documentation Pattern

```gdscript
# Parallax layer corrected for camera start: (640, 360)
# motion_scale: (0.2, 0.2)
# Formula: position = desired_runtime_position + (camera * motion_scale)
# Calculation: (0, 0) + (640 * 0.2, 360 * 0.2) = (128, 72)
```

Clear documentation shows calculation method.

## Common Issues Fixed

| Issue | Cause | Solution |
|-------|-------|----------|
| Layer shifted left in game | position = (0,0), motion_scale < 1 | Add offset: camera * motion_scale |
| Layer jumps at start | Editor position ≠ runtime position | Sync positions using formula |
| Layers misaligned | Different camera assumptions | Consistent camera start position |
| Preview doesn't match game | Parallax math not considered | Apply inverse parallax formula |

## Performance Considerations

Parallax systems are efficient but:
- Limit number of layers (typically 3-5)
- Use TextureRect for static backgrounds
- Consider motion_mirroring for infinite scrolling
- Avoid heavy shaders on parallax layers

Syncing positions doesn't affect performance, only editor usability.

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/asreonn) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-13 -->

