Claude
Skills
Sign in
Back

recordly-screen-recorder

Included with Lifetime
$97 forever

```markdown

Writing & Docs

What this skill does

```markdown
---
name: recordly-screen-recorder
description: Expertise in using and extending Recordly, an open-source Electron screen recorder and editor with auto-zoom, cursor animations, and cross-platform capture.
triggers:
  - add zoom animation to screen recording
  - recordly cursor animation setup
  - screen studio alternative open source
  - recordly electron recording project
  - auto zoom screen recorder typescript
  - recordly export mp4 gif
  - recordly plugin contribution
  - build recordly from source
---

# Recordly Screen Recorder Skill

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

Recordly is a free, open-source, cross-platform screen recorder and editor built on Electron and TypeScript. It mimics Screen Studio's auto-zoom and cursor animation features, uses PixiJS for scene rendering, and provides platform-native capture (ScreenCaptureKit on macOS, WGC on Windows, Electron capture on Linux).

---

## Installation & Setup

### Prerequisites
- Node.js 18+
- npm 9+
- Git

### Clone and Run

```bash
git clone https://github.com/webadderall/Recordly.git recordly
cd recordly
npm install
npm run dev
```

### Build for Distribution

```bash
npm run build          # Compile TypeScript
npm run dist           # Package with electron-builder
```

### macOS Quarantine Fix (unsigned local builds)

```bash
xattr -rd com.apple.quarantine /Applications/Recordly.app
```

### Prebuilt Releases

Download from: `https://github.com/webadderall/Recordly/releases`

---

## Project Architecture

```
recordly/
├── src/
│   ├── main/           # Electron main process (capture, IPC, native helpers)
│   ├── renderer/       # Electron renderer process (editor UI, PixiJS pipeline)
│   ├── shared/         # Types, constants shared between main and renderer
│   └── native/         # Platform-specific native helpers (macOS SCK, Windows WGC)
├── public/             # Static assets
├── CONTRIBUTING.md
└── package.json
```

- **Main process**: Orchestrates recording start/stop, invokes native capture helpers, manages `.recordly` project files.
- **Renderer process**: Hosts the timeline editor, PixiJS scene compositor, cursor overlay pipeline.
- **PixiJS pipeline**: All zoom, cursor, pan, and speed-change effects are composed and rendered here — both in the live preview and during export.

---

## Key Concepts

### `.recordly` Project File

A `.recordly` file is a JSON bundle containing:

```typescript
interface RecordlyProject {
  version: string;
  sourceVideoPath: string;      // Absolute path to raw captured video
  cursorData: CursorEvent[];    // Array of {x, y, timestamp, type} events
  zoomRegions: ZoomRegion[];
  speedRegions: SpeedRegion[];
  trimIn: number;               // Seconds
  trimOut: number;              // Seconds
  frameStyle: FrameStyle;
  audioTrack?: string;          // Path to audio file
  exportSettings: ExportSettings;
}
```

### Cursor Events

```typescript
interface CursorEvent {
  x: number;          // Normalised 0–1 across display width
  y: number;          // Normalised 0–1 across display height
  timestamp: number;  // Milliseconds from recording start
  type: 'move' | 'click' | 'scroll';
  button?: 'left' | 'right' | 'middle';
}
```

### Zoom Regions

```typescript
interface ZoomRegion {
  id: string;
  startTime: number;    // Seconds in video timeline
  endTime: number;
  targetX: number;      // Normalised 0–1 centre of zoom target
  targetY: number;
  scale: number;        // e.g. 2.0 = 2× zoom
  easing: 'ease-in-out' | 'spring' | 'linear';
}
```

### Speed Regions

```typescript
interface SpeedRegion {
  id: string;
  startTime: number;
  endTime: number;
  speed: number;   // 0.25 = slow-mo, 2.0 = double speed
}
```

---

## IPC API (Main ↔ Renderer)

Recordly uses Electron's `ipcMain` / `ipcRenderer` for all cross-process communication.

### Start Recording

```typescript
// renderer → main
ipcRenderer.invoke('recording:start', {
  sourceId: 'screen:0',          // Electron desktopCapturer source ID
  audioMode: 'microphone',       // 'microphone' | 'system' | 'both' | 'none'
  hideSystemCursor: true,
});
```

### Stop Recording

```typescript
// renderer → main
const result = await ipcRenderer.invoke('recording:stop');
// result: { videoPath: string, cursorDataPath: string }
```

### Open Project

```typescript
// renderer → main
const project: RecordlyProject = await ipcRenderer.invoke('project:open', filePath);
```

### Save Project

```typescript
// renderer → main
await ipcRenderer.invoke('project:save', {
  filePath: '/path/to/output.recordly',
  project: projectState,
});
```

### Export

```typescript
// renderer → main
await ipcRenderer.invoke('export:start', {
  project: projectState,
  outputPath: '/path/to/output.mp4',
  format: 'mp4',           // 'mp4' | 'gif'
  quality: 'high',         // 'low' | 'medium' | 'high'
  aspectRatio: '16:9',
});
```

---

## PixiJS Rendering Pipeline

The editor and exporter share the same PixiJS scene graph. To add a new visual effect:

```typescript
import * as PIXI from 'pixi.js';

// Access the shared app instance (renderer process)
import { getPixiApp } from '@/renderer/pixi/appInstance';

const app = getPixiApp();

// Add a custom overlay layer
const overlayContainer = new PIXI.Container();
overlayContainer.name = 'myCustomEffect';
app.stage.addChild(overlayContainer);

// Hook into the render tick
app.ticker.add((delta) => {
  const currentTime = getPlayheadTime(); // from timeline store
  // Update your effect based on currentTime
  overlayContainer.alpha = computeEffectAlpha(currentTime);
});
```

### Cursor Overlay Pattern

```typescript
import * as PIXI from 'pixi.js';
import { useCursorStore } from '@/renderer/stores/cursorStore';

const cursorSprite = PIXI.Sprite.from('/assets/cursors/macos-default.png');
cursorSprite.anchor.set(0, 0);

app.ticker.add(() => {
  const { smoothedX, smoothedY, isClicking } = useCursorStore.getState();
  
  cursorSprite.x = smoothedX * app.screen.width;
  cursorSprite.y = smoothedY * app.screen.height;
  cursorSprite.scale.set(isClicking ? 0.85 : 1.0); // click bounce
});
```

---

## Adding Auto-Zoom Suggestions

Zoom suggestions are generated from cursor activity density:

```typescript
import type { CursorEvent, ZoomRegion } from '@/shared/types';

export function generateZoomSuggestions(
  cursorEvents: CursorEvent[],
  videoDuration: number,
  options = { minDwell: 0.8, zoomScale: 2.2 }
): ZoomRegion[] {
  const suggestions: ZoomRegion[] = [];
  const windowSize = 0.5; // seconds

  for (let t = 0; t < videoDuration; t += windowSize) {
    const window = cursorEvents.filter(
      (e) => e.timestamp / 1000 >= t && e.timestamp / 1000 < t + windowSize
    );

    if (window.length < 3) continue;

    const clicks = window.filter((e) => e.type === 'click');
    if (clicks.length === 0) continue;

    const avgX = clicks.reduce((s, e) => s + e.x, 0) / clicks.length;
    const avgY = clicks.reduce((s, e) => s + e.y, 0) / clicks.length;

    suggestions.push({
      id: crypto.randomUUID(),
      startTime: t,
      endTime: t + options.minDwell,
      targetX: avgX,
      targetY: avgY,
      scale: options.zoomScale,
      easing: 'ease-in-out',
    });
  }

  return suggestions;
}
```

---

## Frame Styling

```typescript
interface FrameStyle {
  background:
    | { type: 'wallpaper'; src: string }
    | { type: 'gradient'; stops: GradientStop[] }
    | { type: 'solid'; color: string };
  padding: number;          // px, applied uniformly
  borderRadius: number;     // px on video frame corners
  shadow: {
    enabled: boolean;
    blur: number;
    offsetY: number;
    color: string;
  };
  blur: number;             // background blur radius
}

// Example usage in project state
const frameStyle: FrameStyle = {
  background: {
    type: 'gradient',
    stops: [
      { offset: 0, color: '#1e1b4b' },
      { offset: 1, color: '#2563eb' },
    ],
  },
  padding: 48,
  borderRadius: 12,
  shadow: { enabled: true, blu

Related in Writing & Docs