# OpenScreen Project File Format: JSON Structure and Electron Persistence

> Discover OpenScreen's project file format. Learn how projects are saved as readable JSON documents using Electron for seamless persistence and easy data management.

- Repository: [Sid/openscreen](https://github.com/siddharthvaddem/openscreen)
- Tags: internals
- Published: 2026-04-03

---

**OpenScreen persists projects as human-readable JSON documents using the `.openscreen` file extension, managed through Electron IPC handlers that serialize `EditorProjectData` objects via `JSON.stringify` and deserialize them with `fs.readFile`.**

The **OpenScreen project file format** defines how the siddharthvaddem/openscreen video editor stores and retrieves editing sessions. Unlike binary formats, OpenScreen uses a schema-backed JSON structure that captures media references, timeline edits, and export settings in a portable text file.

## OpenScreen Project File Structure

Every `.openscreen` file adheres to the `EditorProjectData` interface defined in [`src/components/video-editor/projectPersistence.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/src/components/video-editor/projectPersistence.ts) (lines 55-60). The document contains three primary properties that preserve the complete editing context.

### EditorProjectData Schema

The JSON structure follows this TypeScript shape:

- **`version`** (`number`): The project format version, currently set to `2` via the `PROJECT_VERSION` constant (line 31 in [`projectPersistence.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/projectPersistence.ts)). This enables backward compatibility handling for legacy projects.
- **`media`** (`ProjectMedia`, optional): References to source recordings including `screenVideoPath` (required) and optional `webcamVideoPath` for picture-in-picture layouts.
- **`editor`** (`ProjectEditorState`): Comprehensive editor configuration including wallpaper paths, crop regions, zoom/trim/speed regions, annotation data, aspect ratio presets, and export quality settings.
- **`videoPath`** (`string`, optional): Legacy field retained for migrating projects from earlier OpenScreen versions.

The human-readable structure means you can inspect project contents with any text editor, debug serialization issues, or generate projects programmatically.

## How OpenScreen Persists Project Data

Persistence operates through Electron's **main-renderer IPC layer**, delegating file system operations to the main process while the renderer handles UI state.

### Saving Projects via IPC

When triggering a save operation, the renderer invokes `window.electronAPI.saveProjectFile()`, which channels through the `save-project-file` handler in [`electron/ipc/handlers.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/electron/ipc/handlers.ts) (lines 71-84 and 93-108).

The handler performs three operations:

1. Opens a native **Save dialog** restricted to the `.openscreen` extension
2. Generates a safe filename if none provided
3. Writes the payload using `JSON.stringify(data, null, 2)` to produce formatted, two-space indented JSON

This pretty-printing ensures the file remains human-readable and version-control friendly.

### Loading Projects from Disk

Loading reverses the process through the `load-project-file` handler (lines 118-132 in [`electron/ipc/handlers.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/electron/ipc/handlers.ts)):

1. Displays a native **Open dialog** filtering for `.openscreen` files
2. Reads the selected file using Node.js `fs.readFile`
3. Parses the JSON buffer and validates the resulting object against the expected `EditorProjectData` shape
4. Returns the deserialized project to the renderer for state restoration

The [`electron/preload.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/electron/preload.ts) file (lines 82-88) exposes these capabilities to the renderer via context-bridge isolation, ensuring secure communication between UI and file system.

## Working with the .openscreen Format in Code

### Creating Project Data

Generate compliant project objects using the factory function from [`projectPersistence.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/projectPersistence.ts):

```typescript
import { createProjectData, PROJECT_VERSION } from "@/components/video-editor/projectPersistence";
import type { ProjectMedia } from "@/lib/recordingSession";

const media: ProjectMedia = { 
  screenVideoPath: "/recordings/screen.webm",
  webcamVideoPath: "/recordings/webcam.webm" 
};

const editorState = {
  wallpaper: "/wallpapers/abstract.jpg",
  shadowIntensity: 0.5,
  showBlur: true,
  motionBlurAmount: 2,
  borderRadius: 12,
  padding: 40,
  cropRegion: { x: 0.1, y: 0.1, width: 0.8, height: 0.8 },
  zoomRegions: [{ startTime: 5, endTime: 10, scale: 1.5 }],
  trimRegions: [],
  speedRegions: [],
  annotationRegions: [],
  aspectRatio: "16:9",
  webcamLayoutPreset: "picture-in-picture",
  webcamPosition: { x: 0.8, y: 0.8 },
  exportQuality: "high",
  exportFormat: "mp4",
  gifFrameRate: 30,
  gifLoop: true,
  gifSizePreset: "large"
};

const project = createProjectData(media, editorState);

```

*Source:* `createProjectData` implementation in **projectPersistence.ts** (lines 87-95).

### Saving from the Renderer

Trigger the native save dialog from React components:

```typescript
const handleSave = async () => {
  const result = await window.electronAPI.saveProjectFile(project, "tutorial-project");
  if (result.success) {
    console.log("Saved to:", result.path);
  } else {
    console.error("Save failed:", result.message);
  }
};

```

*Source:* `save-project-file` handler in **ipc/handlers.ts** (lines 71-84).

### Loading a Project

Restore editing sessions asynchronously:

```typescript
const loadProject = async () => {
  const result = await window.electronAPI.loadProjectFile();
  if (result.success) {
    const loaded: EditorProjectData = result.project;
    // Restore media and editor state from loaded.version, loaded.media, loaded.editor
  } else {
    console.error("Load failed:", result.message);
  }
};

```

*Source:* `load-project-file` handler in **ipc/handlers.ts** (lines 118-132).

### Example File Contents

A saved `project.openscreen` file contains formatted JSON:

```json
{
  "version": 2,
  "media": {
    "screenVideoPath": "/recordings/screen.webm",
    "webcamVideoPath": "/recordings/webcam.webm"
  },
  "editor": {
    "wallpaper": "/wallpapers/abstract.jpg",
    "shadowIntensity": 0.5,
    "showBlur": true,
    "motionBlurAmount": 2,
    "borderRadius": 12,
    "padding": 40,
    "cropRegion": { "x": 0.1, "y": 0.1, "width": 0.8, "height": 0.8 },
    "zoomRegions": [{ "startTime": 5, "endTime": 10, "scale": 1.5 }],
    "trimRegions": [],
    "speedRegions": [],
    "annotationRegions": [],
    "aspectRatio": "16:9",
    "webcamLayoutPreset": "picture-in-picture",
    "webcamPosition": { "x": 0.8, "y": 0.8 },
    "exportQuality": "high",
    "exportFormat": "mp4",
    "gifFrameRate": 30,
    "gifLoop": true,
    "gifSizePreset": "large"
  }
}

```

## Summary

- **OpenScreen project file format** uses standard JSON with the `.openscreen` extension, defined by the `EditorProjectData` interface in [`projectPersistence.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/projectPersistence.ts).
- The current schema version is `2`, tracked by the `PROJECT_VERSION` constant (line 31).
- Persistence flows through Electron IPC: `save-project-file` writes via `JSON.stringify(..., null, 2)` while `load-project-file` reads using `fs.readFile` and `JSON.parse`.
- The format stores media references separately from editor state, enabling non-destructive editing workflows.
- Legacy projects containing the single `videoPath` field remain compatible through the optional schema property.

## Frequently Asked Questions

### What file extension does OpenScreen use?

OpenScreen uses the **`.openscreen`** extension to identify project files. The Electron save dialog automatically appends this extension, and the open dialog filters the file picker to show only `.openscreen` files, preventing accidental loading of unrelated JSON documents.

### Is the OpenScreen project file format human-readable?

Yes. The format uses **pretty-printed JSON** with two-space indentation generated by `JSON.stringify(data, null, 2)`. This allows developers and users to inspect project contents, debug media path issues, or manually edit configuration values using standard text editors.

### What happens if I open an older project version?

The `version` property (currently `2`) enables forward compatibility. When loading, the `load-project-file` handler in [`electron/ipc/handlers.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/electron/ipc/handlers.ts) (lines 118-132) parses the JSON and passes the object to the renderer, which can detect legacy versions (such as those using the single `videoPath` field instead of the `media` object) and migrate data accordingly.

### Where is the project schema defined?

The TypeScript interfaces defining the **OpenScreen project file format** reside in [`src/components/video-editor/projectPersistence.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/src/components/video-editor/projectPersistence.ts). Key definitions include `EditorProjectData` (lines 55-60), `ProjectMedia`, and `ProjectEditorState`, while the actual file I/O logic implementing the persistence mechanism lives in [`electron/ipc/handlers.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/electron/ipc/handlers.ts) (lines 71-132).