> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Vite (JavaScript)

Este tutorial te guiará para crear un cliente web que pueda interactuar con un agente de ElevenLabs. Aprenderás a implementar conversaciones por voz en tiempo real, para que usuarios puedan hablar con un agente de IA capaz de escuchar, comprender y responder de forma natural mediante síntesis de voz.

> **Note**
>
> ¿Quieres desarrollar con React/Next.js? Consulta nuestra [guía de Next.js](/docs/es/eleven-agents/guides/quickstarts/next-js)

## Lo que necesitas

1. Un agente de ElevenLabs creado siguiendo [esta guía](/docs/es/eleven-agents/quickstart)
2. Tener `npm` instalado en tu sistema local
3. Conocimientos básicos de JavaScript

## Configuración del proyecto

#### Crea un directorio para el proyecto

Abre una terminal y crea un directorio nuevo para tu proyecto:

```bash
mkdir elevenlabs-conversational-ai
cd elevenlabs-conversational-ai
```

#### Inicializa npm e instala las dependencias

Inicializa un proyecto de npm e instala los paquetes necesarios:

```bash
npm init -y
npm install vite @elevenlabs/client
```

#### Configura la estructura básica del proyecto

Añade esto a tu `package.json`:

**`package.json`**

```json package.json {4}
{
    "scripts": {
        ...
        "dev:frontend": "vite"
    }
}
```

Crea la siguiente estructura de archivos:

```shell {2,3}
elevenlabs-conversational-ai/
├── index.html
├── script.js
├── package-lock.json
├── package.json
└── node_modules
```

## Implementa la interfaz de chat de voz

#### Crea la interfaz HTML

En `index.html`, configura una interfaz de usuario sencilla:

![](/docs/_fern-img/5ad9dea65eddc50beaf444e5d319dc5094df0f4183e50f7ed01900394b3ff9d2.webp)

**`index.html`**

```html index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>ElevenLabs Agents</title>
    </head>
    <body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
        <h1>ElevenLabs Agents</h1>
        <div style="margin-bottom: 20px;">
            <button id="startButton" style="padding: 10px 20px; margin: 5px;">Start Conversation</button>
            <button id="stopButton" style="padding: 10px 20px; margin: 5px;" disabled>Stop Conversation</button>
        </div>
        <div style="font-size: 18px;">
            <p>Status: <span id="connectionStatus">Disconnected</span></p>
            <p>Agent is <span id="agentStatus">listening</span></p>
        </div>
        <script type="module" src="../images/script.js"></script>
    </body>
</html>
```

#### Implementa la lógica de la conversación

En `script.js`, implementa la funcionalidad:

**`script.js`**

```javascript script.js
import { Conversation } from '@elevenlabs/client';

const startButton = document.getElementById('startButton');
const stopButton = document.getElementById('stopButton');
const connectionStatus = document.getElementById('connectionStatus');
const agentStatus = document.getElementById('agentStatus');

let conversation;

async function startConversation() {
    try {
        // Request microphone permission
        await navigator.mediaDevices.getUserMedia({ audio: true });

        // Start the conversation
        conversation = await Conversation.startSession({
            agentId: 'YOUR_AGENT_ID', // Replace with your agent ID
            onConnect: () => {
                connectionStatus.textContent = 'Connected';
                startButton.disabled = true;
                stopButton.disabled = false;
            },
            onDisconnect: () => {
                connectionStatus.textContent = 'Disconnected';
                startButton.disabled = false;
                stopButton.disabled = true;
            },
            onError: (error) => {
                console.error('Error:', error);
            },
            onModeChange: (mode) => {
                agentStatus.textContent = mode.mode === 'speaking' ? 'speaking' : 'listening';
            },
        });
    } catch (error) {
        console.error('Failed to start conversation:', error);
    }
}

async function stopConversation() {
    if (conversation) {
        await conversation.endSession();
        conversation = null;
    }
}

startButton.addEventListener('click', startConversation);
stopButton.addEventListener('click', stopConversation);
```

#### Inicia el servidor de frontend

```shell
npm run dev:frontend
```

> **Note**
>
> Asegúrate de sustituir `'YOUR_AGENT_ID'` por el ID real de tu agente de ElevenLabs.

#### (Opcional) Autentícate con una URL firmada

> **Note**
>
> Este paso de autenticación solo es necesario para agentes privados. Si usas un agente público, puedes omitir esta sección y usar directamente `agentId` en la llamada a `startSession`.

#### Crea variables de entorno

Crea un archivo `.env` en la raíz de tu proyecto:

**`.env`**

```env .env
ELEVENLABS_API_KEY=your-api-key-here
AGENT_ID=your-agent-id-here
```

> **Warning**
>
> Asegúrate de añadir `.env` a tu archivo `.gitignore` para evitar confirmar accidentalmente credenciales confidenciales.

#### Configura el backend

1. Instala dependencias adicionales:

```bash
npm install express cors dotenv
```

2. Crea una carpeta nueva llamada `backend`:

```shell {2}
elevenlabs-conversational-ai/
├── backend
...
```

#### Crea el servidor

**`backend/server.js`**

```javascript backend/server.js
require("dotenv").config();

const express = require("express");
const cors = require("cors");

const app = express();
app.use(cors());
app.use(express.json());

const PORT = process.env.PORT || 3001;

app.get("/api/get-signed-url", async (req, res) => {
    try {
        const response = await fetch(
            `https://api.elevenlabs.io/v1/convai/conversation/get-signed-url?agent_id=${process.env.AGENT_ID}`,
            {
                headers: {
                    "xi-api-key": process.env.ELEVENLABS_API_KEY,
                },
            }
        );

        if (!response.ok) {
            throw new Error("Failed to get signed URL");
        }

        const data = await response.json();
        res.json({ signedUrl: data.signed_url });
    } catch (error) {
        console.error("Error:", error);
        res.status(500).json({ error: "Failed to generate signed URL" });
    }
});

app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});
```

#### Actualiza el código del cliente

Modifica tu `script.js` para obtener y usar la URL firmada:

**`script.js`**

```javascript script.js {2-10,16,19,20}
// ... existing imports and variables ...

async function getSignedUrl() {
    const response = await fetch('http://localhost:3001/api/get-signed-url');
    if (!response.ok) {
        throw new Error(`Failed to get signed url: ${response.statusText}`);
    }
    const { signedUrl } = await response.json();
    return signedUrl;
}

async function startConversation() {
    try {
        await navigator.mediaDevices.getUserMedia({ audio: true });

        const signedUrl = await getSignedUrl();

        conversation = await Conversation.startSession({
            signedUrl,
            // agentId has been removed...
            onConnect: () => {
                connectionStatus.textContent = 'Connected';
                startButton.disabled = true;
                stopButton.disabled = false;
            },
            onDisconnect: () => {
                connectionStatus.textContent = 'Disconnected';
                startButton.disabled = false;
                stopButton.disabled = true;
            },
            onError: (error) => {
                console.error('Error:', error);
            },
            onModeChange: (mode) => {
                agentStatus.textContent = mode.mode === 'speaking' ? 'speaking' : 'listening';
            },
        });
    } catch (error) {
        console.error('Failed to start conversation:', error);
    }
}

// ... rest of the code ...
```

> **Warning**
>
> Las URL firmadas caducan tras un breve periodo. Sin embargo, las conversaciones iniciadas antes de que caduquen continuarán sin interrupciones. En un entorno de producción, implementa una gestión de errores adecuada y lógica de renovación de URL para iniciar conversaciones nuevas.

#### Actualiza el package.json

**`package.json`**

```json package.json {4,5}
{
    "scripts": {
        ...
        "dev:backend": "node backend/server.js",
        "dev": "npm run dev:frontend & npm run dev:backend"
    }
}
```

#### Ejecuta la aplicación

Inicia la aplicación con:

```bash
npm run dev
```

## Próximos pasos

Ahora que tienes una implementación básica, puedes:

1. Añadir comentarios visuales para la actividad de voz
2. Implementar gestión de errores y lógica de reintentos
3. Añadir una vista del historial de chat
4. Personalizar la interfaz de usuario para que encaje con tu marca

> **Info**
>
> Para conocer funciones más avanzadas y opciones de personalización, consulta el
> paquete [@elevenlabs/client](https://www.npmjs.com/package/@elevenlabs/client).