decentraland / decentraland/creator-hub
Inspector snapshots
- Dominant language
- TypeScript
- Stars
- 7
- Forks
- 14
- Avg merge
- 2d 8h
- Merged PRs (30d)
- 44
Description
The inspector should take snapshots of the .composite file regularly
These backup points would be available across sessions, so more powerful than doing UNDO
And these snapshots would be taken periodically every x time, so more practical than having to commit changes to GitHub every few edits.
We need to design a UI to let creators navigate the history of snapshots and revert their scene back to that state.
And how do I come back from an old snapshot?
For a first iteration, we should only worry about taking snapshots of the .composite file.
# Scene Snapshots & Checkpoints - Feature Proposal
## Executive Summary
Scene Snapshots is a feature that complements the existing undo/redo system by providing persistent, labeled save points for scenes. This addresses a critical gap: while creators can undo changes within a session, they cannot recover work after app crashes, return to previous versions from days ago, or safely experiment with major changes.
**Impact**: High - Prevents data loss, increases user confidence, enables experimentation
---
## Problem Statement
### Current State
Creator Hub currently has:
- ✅ Undo/redo system within the inspector
- ✅ Session-based change history
### The Gap
- ❌ **No persistent history** - undo/redo is lost when app closes
- ❌ **No recovery from crashes** - all work since last manual save is lost
- ❌ **No version comparison** - can't see what changed between versions
- ❌ **No labeled checkpoints** - can't mark important milestones
- ❌ **No safe experimentation** - users fear trying new things
### User Pain Points
**Scenario 1: App Crash**
> "I was working on my scene for 3 hours. The app crashed. When I reopened it, everything since my last save was gone. I almost gave up."
**Scenario 2: Experimentation**
> "I want to try a completely different lighting setup, but I'm afraid if I don't like it, I can't get back to what I had."
**Scenario 3: Collaboration**
> "My teammate made changes yesterday. I want to see what the scene looked like before they started working on it."
**Scenario 4: Accidental Deletion**
> "I accidentally deleted a complex entity hierarchy. Undo didn't go back far enough. I had to rebuild it from scratch."
---
## Proposed Solution
### Feature Overview
**Scene Snapshots** is a persistent versioning system that automatically and manually saves complete scene states with the following capabilities:
1. **Auto-Snapshots** - Automatic saves at regular intervals
2. **Manual Checkpoints** - User-created save points with custom labels
3. **Snapshot Browser** - Visual timeline of all snapshots
4. **One-Click Restore** - Restore any snapshot instantly
5. **Snapshot Comparison** - See what changed between versions (future enhancement)
### Core Features
#### 1. Auto-Snapshots
- Automatically save scene state every 10 minutes (configurable)
- Triggered before risky operations:
- Before bulk delete operations
- Before importing large assets
- Before major scene structure changes
- Retention policy: Keep last 20 auto-snapshots (configurable)
- Storage: `.snapshots/` folder in scene directory
#### 2. Manual Checkpoints
- User-initiated save points with custom labels
- Examples: "Final lighting", "Before terrain experiment", "Ready for review"
- Never auto-deleted (user must manually delete)
- Optional description field for detailed notes
- Keyboard shortcut: `Cmd+Shift+S` (Mac) / `Ctrl+Shift+S` (Windows)
#### 3. Snapshot Browser
- Chronological list of all snapshots
- Grouped by date (Today, Yesterday, This Week, Older)
- Shows:
- Timestamp
- Label (manual checkpoints only)
- Snapshot type (auto/manual)
- Scene thumbnail preview
- Basic metadata (entity count, file size)
- Actions:
- Restore snapshot
- Delete snapshot
- Star/favorite important snapshots
- Add/edit label (for auto-snapshots)
#### 4. Restore Flow
- Click "Restore" on any snapshot
- Confirmation dialog showing:
- What will be restored
- Warning that current state will be saved as new snapshot first
- Option to "Compare with current" (future)
- One-click restore
- Current state automatically saved before restoring (safety net)
---
## User Interface
### Snapshots Modal
```
┌──────────────────────────────────────────────────────────┐
│ Scene Snapshots [×] │
├──────────────────────────────────────────────────────────┤
│ │
│ [📸 Create Snapshot ] │
│ │
├──────────────────────────────────────────────────────────┤
│ 🔍 Search snapshots... Sort: Newest │
├──────────────────────────────────────────────────────────┤
│ │
│ 📅 TODAY │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 📸 2:30 PM - "Final lighting setup" ⭐ │ │
│ │ Manual checkpoint • 234 entities • 12.3 MB │ │
│ │ [Restore] [Delete] [Compare] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 🔄 1:45 PM - Auto-save │ │
│ │ Auto snapshot • 234 entities • 12.3 MB │ │
│ │ [Restore] [Delete] [Add Label] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 🔄 1:35 PM - Auto-save │ │
│ │ Auto snapshot • 229 entities • 11.8 MB │ │
│ │ [Restore] [Delete] [Add Label] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 📅 YESTERDAY │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 📸 5:20 PM - "Before terrain changes" ⭐ │ │
│ │ Manual checkpoint • 198 entities • 9.2 MB │ │
│ │ [Restore] [Delete] [Compare] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 🔄 3:10 PM - Auto-save │ │
│ │ Auto snapshot • 195 entities • 9.1 MB │ │
│ │ [Restore] [Delete] [Add Label] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ [Show older snapshots...] │
│ │
└──────────────────────────────────────────────────────────┘
```
### Create Checkpoint Modal
```
┌──────────────────────────────────────────┐
│ Create Checkpoint [×] │
├──────────────────────────────────────────┤
│ │
│ Label (required) │
│ ┌─────────────────────────────────────┐ │
│ │ Final lighting setup │ │
│ └─────────────────────────────────────┘ │
│ │
│ Description (optional) │
│ ┌─────────────────────────────────────┐ │
│ │ Finished adjusting all directional │ │
│ │ lights and added ambient occlusion │ │
│ │ │ │
│ └─────────────────────────────────────┘ │
│ │
│ Scene Stats: │
│ • 234 entities │
│ • 45 unique assets │
│ • 12.3 MB total size │
│ │
│ [Cancel] [Create Checkpoint] │
│ │
└──────────────────────────────────────────┘
```
### Restore Confirmation Dialog
```
┌──────────────────────────────────────────┐
│ Restore Snapshot? [×] │
├──────────────────────────────────────────┤
│ │
│ You're about to restore: │
│ │
│ 📸 "Final lighting setup" │
│ Created: Today at 2:30 PM │
│ 234 entities • 12.3 MB │
│ │
│ ⚠️ Your current scene will be saved │
│ as a new auto-snapshot first │
│ │
│ Changes since this snapshot: │
│ • +5 entities added │
│ • 3 entities modified │
│ • 2 assets imported │
│ │
│ [Cancel] [Restore] │
│ │
└──────────────────────────────────────────┘
```
### Status Indicator
Add a small indicator in the inspector header:
```
┌────────────────────────────────────────────┐
│ Scene: My Awesome Scene 💾 Saved │
│ Last: 2 min ago │
└────────────────────────────────────────────┘
```
Shows:
- 💾 "Saved" - All changes saved
- 💾 "Saving..." - Currently saving
- ⚠️ "Unsaved changes" - Changes since last snapshot
- 📸 "Checkpoint created" - Brief confirmation
---
## Technical Implementation
### Data Structure
#### Snapshot Metadata File
Location: `/.snapshots/metadata.json`
```json
{
"version": "1.0.0",
"snapshots": [
{
"id": "snap_20231230_143000_auto",
"type": "auto",
"timestamp": "2023-12-30T14:30:00Z",
"label": null,
"description": null,
"starred": false,
"stats": {
"entityCount": 234,
"assetCount": 45,
"fileSizeBytes": 12894720
},
"thumbnailPath": ".snapshots/snap_20231230_143000_auto.jpg",
"dataPath": ".snapshots/snap_20231230_143000_auto.scene.json"
},
{
"id": "snap_20231230_142000_manual",
"type": "manual",
"timestamp": "2023-12-30T14:20:00Z",
"label": "Final lighting setup",
"description": "Finished adjusting all directional lights...",
"starred": true,
"stats": {
"entityCount": 229,
"assetCount": 43,
"fileSizeBytes": 12345678
},
"thumbnailPath": ".snapshots/snap_20231230_142000_manual.jpg",
"dataPath": ".snapshots/snap_20231230_142000_manual.scene.json"
}
]
}
```
#### Snapshot Data File
Location: `/.snapshots/snap__.scene.json`
```json
{
"version": "1.0.0",
"sceneJson": { /* complete scene.json content */ },
"compositeJson": { /* complete composite entities */ },
"assetManifest": [
{
"id": "asset_123",
"path": "models/tree.glb",
"hash": "sha256:abc123..."
}
]
}
```
### File System Structure
```
my-scene/
├── scene.json
├── tsconfig.json
├── package.json
└── .snapshots/
├── metadata.json
├── snap_20231230_143000_auto.scene.json
├── snap_20231230_143000_auto.jpg
├── snap_20231230_142000_manual.scene.json
├── snap_20231230_142000_manual.jpg
└── ...
```
### Auto-Snapshot Trigger Logic
```typescript
// Trigger conditions
const shouldAutoSnapshot = (
timeSinceLastSnapshot: number,
pendingChanges: number,
riskLevel: 'low' | 'medium' | 'high'
): boolean => {
// Time-based: Every 10 minutes if changes exist
if (timeSinceLastSnapshot > 10 * 60 * 1000 && pendingChanges > 0) {
return true;
}
// Risk-based: Before dangerous operations
if (riskLevel === 'high') {
return true;
}
// Change-based: After 50+ entity changes
if (pendingChanges > 50) {
return true;
}
return false;
};
// High-risk operations
const HIGH_RISK_OPERATIONS = [
'bulk_delete', // Deleting multiple entities
'clear_scene', // Clear all entities
'import_large_asset', // Importing >50MB asset
'replace_terrain', // Replacing terrain
];
```
### Snapshot Creation Flow
```typescript
async function createSnapshot(
type: 'auto' | 'manual',
label?: string,
description?: string
): Promise {
// 1. Generate snapshot ID
const id = generateSnapshotId(type);
// 2. Capture current scene state
const sceneData = await captureSceneState();
// 3. Generate thumbnail
const thumbnail = await captureSceneThumbnail();
// 4. Calculate stats
const stats = calculateSceneStats(sceneData);
// 5. Save snapshot files
await saveSnapshotData(id, sceneData);
await saveSnapshotThumbnail(id, thumbnail);
// 6. Update metadata
const snapshot: Snapshot = {
id,
type,
timestamp: new Date().toISOString(),
label,
description,
starred: false,
stats,
thumbnailPath: `.snapshots/${id}.jpg`,
dataPath: `.snapshots/${id}.scene.json`
};
await updateSnapshotMetadata(snapshot);
// 7. Cleanup old auto-snapshots if needed
if (type === 'auto') {
await cleanupOldAutoSnapshots();
}
return snapshot;
}
```
### Restore Flow
```typescript
async function restoreSnapshot(snapshotId: string): Promise {
// 1. Load snapshot data
const snapshot = await loadSnapshot(snapshotId);
// 2. Create auto-snapshot of current state first (safety net)
await createSnapshot('auto', 'Before restore');
// 3. Validate snapshot data
if (!validateSnapshotData(snapshot)) {
throw new Error('Invalid snapshot data');
}
// 4. Apply snapshot to scene
await applySnapshotToScene(snapshot);
// 5. Reload inspector
await reloadInspector();
// 6. Show success notification
showNotification('Snapshot restored successfully');
}
```
### Settings Integration
Add to App Settings modal:
```typescript
interface SnapshotSettings {
autoSnapshotEnabled: boolean;
autoSnapshotIntervalMinutes: number; // 5, 10, 15, 30
maxAutoSnapshots: number; // 10, 20, 50, 100
captureSceneThumbnails: boolean;
confirmBeforeRestore: boolean;
}
```
---
## Storage & Performance Considerations
### Storage Optimization
1. **Compression**: Use gzip compression for snapshot data (reduces size by ~70%)
2. **Deduplication**: Store only changed entities (delta snapshots) - Phase 2
3. **Asset References**: Don't duplicate assets, only reference them
4. **Cleanup Policy**:
- Keep last 20 auto-snapshots (configurable)
- Never auto-delete manual checkpoints
- Warn user if `.snapshots/` folder exceeds 500MB
### Performance Optimization
1. **Async Operations**: All snapshot operations run in background
2. **Non-Blocking**: Creating snapshots doesn't freeze UI
3. **Debouncing**: Auto-snapshots debounced to prevent spam
4. **Lazy Loading**: Load snapshot list metadata only, load full data on demand
5. **Thumbnail Cache**: Cache thumbnails in memory for fast browsing
### Estimated Storage
Example scene: 200 entities, 40 assets
- Scene JSON: ~500KB
- Compressed: ~150KB
- Thumbnail: ~50KB
- **Total per snapshot**: ~200KB
- **20 auto-snapshots**: ~4MB
- **10 manual checkpoints**: ~2MB
- **Total**: ~6MB per scene
---
## Open Questions
1. **Storage limits**: Should we enforce hard limits on snapshot storage?
2. **Cloud sync**: Should Phase 2 include optional cloud backup?
3. **Team collaboration**: How do snapshots work with multiple team members?
4. **Migration**: Should we migrate existing scenes to have snapshots?
5. **Export format**: What format for exporting snapshots for sharing?
---
## Appendix
### Related Features
- Undo/Redo system (existing)
- Scene save/load (existing)
- Auto-save (existing)
- Git integration (future)
### Similar Features in Other Tools
- **Unity**: Version Control (Plastic SCM)
- **Unreal**: Source Control integration
- **Blender**: Auto-save + version recovery
- **Figma**: Version history
- **Google Docs**: Revision history
## Other topics that were also discussed
- Storing backups of all 3d models and assets as part of the snapshot. This would be useful in case I deleted assets.
Super useful, but can be left out of the first iteration
- Storage limits: Should we enforce hard limits on snapshot storage? Since this is on the local file-system and these backups aren't uploaded, it's not a big priority to keep a small footprint
- Export format: What format for exporting snapshots for sharing?
los snapshots deben ser idempotentes, por lo que no deberia haber problemas si se exporta un snapshot entre distintos colaboradores (un .zip con la estructura de carpetas y la metadata, composite, scene, assets)
All those considerations are for future iterations
We also don't need to migrate existing projects to start using this. Snapshots will be generated automatically
Contributor guide
Assessment
This issue has not been assessed yet.