---
title: StudioKit Package
description: Shared TypeScript utilities for Studio-related objects across client and server
---

import { Callout, Steps, FileTree, Tabs } from "nextra/components";

# StudioKit Package

**StudioKit** (`@suno/studiokit`) is a shared TypeScript package that provides universal utilities for manipulating and analyzing Studio-related objects. It enables code reuse between client-side UI and server-side rendering without duplication.

<Callout type="info" emoji="🎯">
  **Goal**: Ship more Studio features faster by maintaining a single source of
  truth for Studio logic that works everywhere—browser, Node.js, and Modal
  workers.
</Callout>

---

## Why StudioKit?

### The Problem

Before StudioKit, identical Studio logic was duplicated across multiple codebases:

- **`ui/app-ui/src/components/studio/`** - Client-side React UI code
- **`dsp-engine/bounce/`** - Server-side Node.js audio rendering
- **`suno_utils/suno_utils/tasks/studio_bounce.py`** - Python Modal workers

When making changes to Studio timing, warping, or clip manipulation logic, developers had to update the same code in 2-3 places with different programming languages and conventions. This led to:

- Bugs from inconsistent implementations
- Slower feature development
- Risky deployments with multiple points of failure

### The Solution

StudioKit extracts core Studio utilities into a single, framework-agnostic TypeScript package that can be imported throughout the monorepo:

```typescript
// Before: Duplicated in multiple files
import { getWarpBeatsFromSeconds } from "../studio/warpUtils";

// After: Single source of truth
import { getWarpBeatsFromSeconds } from "@suno/studiokit";
```

<Callout type="important" emoji="✨">
  StudioKit is **universal** by design—no browser or Node.js-specific APIs,
  making it compatible with any JavaScript runtime.
</Callout>

---

## Usage

### Installation

StudioKit is part of the monorepo's pnpm workspace and automatically available to all TypeScript packages.

<Callout type="info">
  The package is referenced in `pnpm-workspace.yaml` as `'studiokit'`.
</Callout>

### Importing

StudioKit uses **sub-path imports** to import from specific modules:

```typescript
// Warp utilities
import {
  getWarpBeatsFromSeconds,
  getWarpSecondsFromBeats,
  getEffectiveMarkers,
  getWarpEnabledAndPopulated,
} from "@suno/studiokit/warpUtils";

// Project state types
import { StudioClip } from "@suno/studiokit/projectState/fixClip";
import { StudioTrack } from "@suno/studiokit/projectState/fixTrack";

// State fixing and serialization
import { fixStudioProjectState } from "@suno/studiokit/projectState/fixStudioProjectState";
import { serializeProjectState } from "@suno/studiokit/projectState/serialization";
```

You can also import from the main package export:

```typescript
import { autoCrossfadeClips, getClipContentBeats } from "@suno/studiokit";
```

### Example: Warp Time Conversion

```typescript
import { getWarpBeatsFromSeconds } from "@suno/studiokit/warpUtils";
import { StudioClip } from "@suno/studiokit/projectState/fixClip";

function getCurrentBeatPosition(clip: StudioClip, playheadSeconds: number) {
  // Convert playhead position (in seconds) to beats, accounting for warp markers
  const beatPosition = getWarpBeatsFromSeconds(clip.warp, playheadSeconds);
  return beatPosition;
}
```

### Example: Fixing Project State

```typescript
import { fixStudioProjectState } from "@suno/studiokit/projectState/fixStudioProjectState";
import { getBufferKeys } from "./uploadedClipCache";

function loadProject(rawState: unknown) {
  // Validate and normalize project state
  const fixedState = fixStudioProjectState(rawState, {
    keepUploads: getBufferKeys(),
  });
  return fixedState;
}
```

---

## Build Integration

StudioKit is integrated into both `app-ui` and `dsp-engine` builds. If you encounter build failures mentioning StudioKit, check the following:

### app-ui

- **Package reference**: `"@suno/studiokit": "workspace:*"` in `package.json`
- **Transpilation**: `transpilePackages: ['@suno/studiokit']` in `next.config.mjs` (required for Next.js to process the TypeScript source)

### dsp-engine/bounce

- **Package reference**: `"@suno/studiokit": "file:../../studiokit"` in `bounce/package.json`
- **Bundling**: esbuild bundles StudioKit during the build step (see `scripts.build` in `package.json`)

---

## Package Structure

```
studiokit/src/
├── projectState/          # Project state validation and fixing
│   ├── fixStudioProjectState.ts  # Main entry point
│   ├── fixTrack.ts        # Track/clip fixing logic
│   ├── fixClip.ts         # Individual clip validation
│   ├── serialization.ts   # State serialization/deserialization
│   ├── warpMarkersRegistry.ts  # Warp marker deduplication
│   └── ...
├── warpUtils.ts           # Warp time conversion utilities
├── getClipContentBeats.ts # Clip content duration calculations
├── autoCrossfadeClips.ts  # Automatic crossfade generation
└── index.ts               # Main exports
```

## Adding New Functionality

To add new utilities to StudioKit:

<Steps>

### Create your utility file

```typescript
// studiokit/src/myNewUtility.ts
import { StudioClip } from "./projectState/fixClip";

export function myNewUtility(clip: StudioClip) {
  // Your logic here
  return clip.startBeats + clip.endBeats;
}
```

### Export from index (optional)

If your utility should be available from the main package:

```typescript
// studiokit/src/index.ts
export * from "./myNewUtility";
```

### Use in the monorepo

Use sub-path imports for direct access, or import from main package if exported:

```typescript
import { myNewUtility } from "@suno/studiokit/myNewUtility";
// or
import { myNewUtility } from "@suno/studiokit";
```

</Steps>

---
