# Node.js Demo App (legacy)

> **Legacy.** This demo uses the LiveKit-based `agent-falcon` backend.
> New builds should use the [WebSocket API](/content/docs/api-reference/index.html) directly.

This is a minimal working example that demonstrates real-time speech translation in a browser.

[GitHub Repository](https://github.com/pinch-eng/pinch-realtime-demo)

## Quick Start

#### 1. Clone the repository

```bash
git clone https://github.com/pinch-eng/pinch-realtime-demo
cd pinch-realtime-demo
```

#### 2. Install Dependencies

```bash
npm install
```

#### 3. Add Your API Key
Create a `.env` file in the project root:

```bash
PINCH_API_KEY=your_api_key_here
```

Don’t have an API key? Login to the [Pinch Developer Portal >](https://portal.startpinch.com/dashboard/developers)

#### 4. Run the Demo

```bash
npm start
```

You’ll see:

EXAMPLE OUTPUT

Pinch Real-Time Demo running at http://localhost:3000

#### 5. Try It Out
1. Open your browser to `http://localhost:3000`
2. Click the **Connect** button
3. Allow microphone access when prompted
4. Start speaking and watch the real-time translation appear!

## How It Works
The demo has two simple parts:

**Backend (server.js)**
- Express server that runs on your computer
- Proxies requests to the Pinch API (keeps your API key secret)
- Serves the HTML file to your browser

**Frontend (index.html)**
- Single HTML file with embedded JavaScript
- Connects to the translation service
- Captures microphone audio and sends it for translation
- Displays transcripts and plays translated audio

## Understanding the Code

#### Session Creation (Backend)
The backend creates a secure session:

```javascript
// server.js
app.post("/api/session", async (req, res) => {
  const response = await fetch("https://api.startpinch.com/api/beta1/session", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${PINCH_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      source_language: "en-US",
      target_language: "es-ES",
      voice_type: "clone"
    })
  });
  const data = await response.json();
  res.json(data);
});
```

#### Connecting to the Room (Frontend)

```javascript
// Get credentials from backend
const { url, token } = await fetch("/api/session", {
  method: "POST"
}).then(r => r.json());

// Connect to LiveKit room
const room = new Room();
await room.connect(url, token);

// Start streaming microphone
await room.localParticipant.setMicrophoneEnabled(true);
```

#### Receiving Transcripts (Frontend)
The browser listens for transcript messages:

```javascript
room.on(RoomEvent.DataReceived, (payload) => {
  const message = JSON.parse(new TextDecoder().decode(payload));

if (message.type === "original_transcript") {
    // Display original text in left panel
    displayOriginal(message.text, message.is_final);
  }
  else if (message.type === "translated_transcript") {
    // Display translation in right panel
    displayTranslated(message.text, message.is_final);
  }
});
```

#### Receiving Audio (Frontend)
The browser plays translated audio automatically:

```javascript
room.on(RoomEvent.TrackSubscribed, (track) => {
  if (track.kind !== Track.Kind.Audio) return;

// Create audio element
  const audioEl = document.createElement("audio");
  audioEl.autoplay = true;
  audioEl.playsInline = true;

// Attach translated audio stream
  track.attach(audioEl);
  document.body.appendChild(audioEl);
});
```

#### Change Languages
Edit `server.js` to translate between different languages:

```javascript
body: JSON.stringify({
  source_language: "en-US",    // Change this
  target_language: "es-ES",    // Change this
  voice_type: "clone"
})
```

Check [Supported languages >](/content/docs/supported-languages/index.html) for the full list.
