Unidad 6
Introducción 📜
¿Qué aprenderás en esta unidad? 💡
Actividad 01
Preparación del entorno y primer contacto
👣 Pasos:
🚀 Tu solución:
Bitácora - Actividad 01: Preparación del entorno
1. Error al ejecutar npm install
y npm start
- Problema: Ambos comandos fallaron porque no se encontró el archivo
package.json
en la ubicación actual (C:\Users\B15520lest
). - Causa: No navegué a la carpeta del proyecto clonado (donde sí está el
package.json
). - Solución: Usar
cd ruta/de/la/carpeta/del/proyecto
antes de ejecutar los comandos.
2. ¿Qué hace npm install
?
Instala las dependencias del proyecto (como Socket.IO y Express) listadas en el package.json
. Sin esto, el servidor no puede funcionar.
3. Mensaje esperado con npm start
Si se ejecuta correctamente, debería aparecer:
Servidor escuchando en http://localhost:3000
Esto confirma que el servidor Node.js está activo.
4. Comportamiento de las páginas (cuando el servidor funciona)
- page1: Círculo rojo y texto “Página 1”.
- page2: Círculo azul y texto “Página 2”.
- Interacción: Al mover una ventana, la otra página refleja el movimiento en tiempo real.
5. Mensajes en el servidor (éxito)
- Al abrir las páginas:
Cliente conectado: [ID único]
- Consola del navegador (F12): Muestra eventos
positionUpdated
con coordenadas.
Investigación 🔎
Actividad 02
El Viaje de los datos - De tu navegador al servidor y de regreso
1. El Gran mapa: ¿Qué es Internet?
Imagina Internet no como una “nube” etérea, sino como una gigantesca red de carreteras y cables
conectando millones de lugares: bibliotecas, tiendas, oficinas, casas… y también unos lugares
especiales llamados Servidores
. Tu computador (o teléfono) es tu vehículo, conectado a
estas carreteras.
2. Tu vehículo y tu destino: navegador y servidor
Tu navegador web (Chrome, Firefox, Safari, Edge…) es tu vehículo súper inteligente. No solo te lleva por las carreteras (Internet), sino que sabe cómo pedir cosas y, lo más importante, ¡cómo mostrarte lo que recibe! Eres tú, el usuario, quien decide a dónde ir. Tú eres el Cliente.
Un Servidor es como una biblioteca o un almacén gigante abierto 24/7, ubicado en algún
punto de esa red de carreteras. Su trabajo principal es “servir” información o funcionalidad
cuando un Cliente
(como tu navegador) se la pide correctamente.
La base: el modelo Cliente-Servidor = el
Cliente
pide, elServidor
responde.
3. La dirección exacta: ¿Qué es una URL?
Para que tu Navegador sepa a qué Servidor
específico ir dentro de esa inmensa red, necesita
una dirección precisa. Esa dirección es la URL (Uniform Resource Locator).
Desglosemos una URL típica: http://www.ejemplo.com/pagina/index.html
http://
- El protocolo. Son las reglas del idioma que usarán tu navegador y el servidor para hablar. ¡Volveremos a esto!
www.ejemplo.com
- El nombre de dominio. Es como el nombre del edificio o de la biblioteca. Detrás de escena, este nombre se traduce a una dirección numérica (la
dirección IP
) que sí indica la ubicación física en la red.
- El nombre de dominio. Es como el nombre del edificio o de la biblioteca. Detrás de escena, este nombre se traduce a una dirección numérica (la
/pagina/index.html
- La ruta específica dentro de ese servidor. Es como pedir ir a la “sección de arte, Estante 3, Libro 5” dentro de la biblioteca. Indica el recurso exacto que quieres.
4. La conversación: protocolo HTTP
Dijimos que http
era el protocolo. ¿Pero qué significa eso? Recuerda las unidades anteriores
donde usaste protocolos (ASCII
, binario con framing
) para que el micro:bit y p5.js se entendieran
por el puerto serial. ¡Aquí es la misma idea, pero a gran escala!
HTTP (HyperText Transfer Protocol) es el conjunto de reglas estándar que usan los Navegadores
(Clientes
) y los Servidores
para comunicarse en la web. Es como un idioma formal:
--- INICIO DE LA CONVERSACIÓN HTTP ---
➡️ Navegador (Cliente) envía una Petición HTTP (HTTP Request): "¡Hola servidor en www.ejemplo.com! SOY un navegador Chrome. POR FAVOR, ¿me puedes dar (método GET) el recurso que está en /pagina/index.html?"
⬅️ Servidor responde con una Respuesta HTTP (HTTP Response): "¡Entendido, Navegador! AQUÍ TIENES (Código de estado 200 OK). El archivo que pediste (index.html) es de tipo HTML (Content-Type). Aquí están sus contenidos: <html>...</html>"
--- FIN DE LA CONVERSACIÓN HTTP ---
5. El Paquete recibido: HTML, CSS y JavaScript
Cuando el servidor responde con éxito a una petición de una página web, normalmente no envía una sola cosa, sino un “paquete” con instrucciones para tu Navegador. Este paquete suele contener tres tipos principales de archivos:
- HTML (HyperText Markup Language):
- El esqueleto, la estructura de la página.
- Define qué elementos hay: títulos, párrafos, imágenes, botones, etc.
- Analogía: el plano de una casa.
- CSS (Cascading Style Sheets):
- La decoración, el estilo visual.
- Define cómo se ven esos elementos: colores, fuentes, tamaños, posiciones.
- Analogía: la pintura, los muebles y las cortinas de la casa.
- JavaScript (JS):
- La interactividad, la “magia” que hace que la página responda a tus acciones.
- Puede cambiar el
HTML
y elCSS
dinámicamente, reaccionar a clics, enviar datos, ¡Y mucho más! - Analogía: la electricidad, la plomería y los electrodomésticos que hacen la casa funcional y habitable.
6. ¡Despierta, JavaScript! ¿Cómo se Ejecuta?
El Navegador recibe el HTML
, lo lee y construye la estructura básica de la página (el DOM - Document Object Model). Luego aplica los estilos del CSS
. Pero, ¿cuándo y cómo entra en juego el JavaScript
?
Normalmente, el HTML
incluye etiquetas <script>
que le dicen al navegador: "Oye, aquí hay código JavaScript, por favor, ejecútalo"
. Esto puede pasar:
- Mientras se carga la página: si el
<script>
está en medio delHTML
, el navegador pausa la construcción, ejecuta elJS
y luego sigue. - Después de cargar el HTML: a menudo, los scripts se colocan al final del
<body>
o se marcan con atributos comodefer
oasync
para que se ejecuten después de que la estructura principal (DOM
) esté lista. ¡Esto es importante para que elJS
pueda encontrar y manipular los elementosHTML
!
Una vez que el JS
está “vivo”, no se ejecuta necesariamente de arriba abajo y termina (como un script simple). En la web, el JS
a menudo funciona de forma dirigida por eventos.
7. El Modelo de ejecución: imperativo vs basado en eventos
Has estado usando p5.js. Recuerda las funciones setup()
(se ejecuta una vez) y draw()
(se ejecuta en bucle constante, ~60 veces por segundo). Este es un modelo bastante imperativo: tú le dices al programa qué hacer paso a paso, y draw()
repite esos pasos continuamente.
El JavaScript
que veremos en el caso de estudio (y en mucha programación web moderna) es más declarativo y basado en eventos. En lugar de un bucle draw()
constante, defines funciones y luego le dices al navegador:
“CUANDO el usuario haga clic en este botón, ENTONCES ejecuta
estaFuncion()
”.“CUANDO llegue un mensaje del servidor con el nombre
getdata
, ENTONCES ejecutaotraFuncion()
”.“CUANDO la ventana cambie de tamaño, ENTONCES ejecuta
aquellaFuncion()
”.
El código no se ejecuta en un bucle predecible, sino que reacciona a eventos que ocurren. El navegador se encarga de detectar esos eventos y llamar a las funciones que tú registraste (llamadas event listeners o manejadores de eventos).
8. El mago detrás del telón: ¿Qué es Node.js?
Hasta ahora, JavaScript
vivía solo dentro de tu Navegador. Node.js fue una idea revolucionaria: ¿Y si pudiéramos tomar el motor de JavaScript súper rápido de Google Chrome (llamado V8
) y usarlo fuera del navegador, directamente en un Servidor
?
¡Eso es Node.js
! Permite a los desarrolladores escribir el código del lado del servidor usando JavaScript
, el mismo lenguaje que ya usan en el lado del cliente (navegador). En nuestro caso de estudio, server.js
es un script de Node.js
que:
- Crea un servidor
HTTP
(como la biblioteca con la sección de historia de la que hablamos antes). - Escucha peticiones de los navegadores (como la tuya pidiendo
/page1
). - Envía los archivos
HTML
,CSS
yJS
necesarios. - Y además, maneja la comunicación en tiempo real (¡Siguiente punto!).
9. WebSockets y Socket.IO
El modelo HTTP
normal de Petición/Respuesta es como enviar correos electrónicos: pides algo, esperas, recibes una respuesta. Funciona bien para pedir páginas web, pero ¿Qué pasa si quieres comunicación instantánea, como un chat o ver la posición del cursor de otra persona en tiempo real? Enviar un “correo” (HTTP Request
) cada décima de segundo sería muy ineficiente.
Aquí entran los WebSockets. Son como establecer una línea telefónica directa y permanente entre el Navegador (Cliente
) y el Servidor
una vez que la conexión inicial se ha hecho. Una vez abierta, ambos pueden enviarse mensajes instantáneamente sin necesidad de nuevas peticiones HTTP
formales.
Socket.IO es una librería (tanto para Node.js
en el servidor como para JavaScript
en el navegador) que hace que usar WebSockets
(y otras técnicas de respaldo si WebSockets
no está disponible) sea mucho más fácil. Nos da funciones simples como:
socket.on('nombreDelMensaje', funcionReceptora)
: para escuchar mensajes del otro lado.socket.emit('nombreDelMensaje', datosAEnviar)
: para enviar mensajes al otro lado.
🚀 Tu solución:
Unidad 6: Viaje de los datos (Cliente-Servidor)**
1. Internet como red física
- Analogía: Internet son carreteras (cables) que conectan “lugares” (servidores). Mi Wi-Fi es la rampa de acceso.
- Sin conexión: Sin Wi-Fi/cable, mi “vehículo” (navegador) no puede llegar a ningún servidor.
2. Modelo Cliente-Servidor
- Ejemplo real: En un restaurante, yo (cliente) pido comida (datos) al mesero (servidor).
- Digital: Navegador (cliente) pide páginas web a un servidor (como Apache o Node.js).
3. Partes de una URL
- Ejemplo:
https://github.com/user/repo
- Protocolo:
https://
(reglas de comunicación). - Dominio:
github.com
(dirección del servidor). - Ruta:
/user/repo
(ubicación específica del recurso).
- Protocolo:
- Sin ruta: El servidor envía la página por defecto (ej:
index.html
).
4. HTTP vs. Serial
- Similitudes: Ambos son protocolos (reglas para comunicarse).
- Diferencias: HTTP incluye metadatos (ej:
Content-Type
), mientras que el serial envía bytes crudos. - Complejidad: HTTP debe manejar múltiples tipos de datos (HTML, imágenes, etc.), no solo números.
5. HTML, CSS y JS
- Login web:
- HTML: Campos de texto y botón (
<input>
,<button>
). - CSS: Color del botón (
background-color: blue;
). - JS: Validar contraseña antes de enviar (
if (password.length > 0)
).
- HTML: Campos de texto y botón (
6. JS basado en eventos
- Ventajas:
- Eficiente: Solo ejecuta código cuando ocurre algo (ej: clic).
- Menos carga: No redibuja la página innecesariamente (como haría
draw()
).
7. Node.js y Socket.IO
- Node.js: Permite usar JS en el servidor. Ventaja: Mismo lenguaje en frontend y backend.
- Socket.IO vs HTTP:
- HTTP: Como correos (petición-respuesta).
- Socket.IO: Como llamada telefónica (comunicación instantánea).
- Aplicaciones: Chats, juegos multijugador, actualizaciones en tiempo real (ej: Google Docs).
eebden: Entendí cómo los datos viajan entre cliente y servidor, y por qué Socket.IO es clave para aplicaciones interactivas.
Actividad 03
El Servidor (Node.js)
1. Dependencias: las herramientas necesarias
const express = require('express');const http = require('http');const socketIO = require('socket.io');const path = require('path');
2. Creación del Servidor y Socket.IO
const app = express();const server = http.createServer(app);const io = socketIO(server);const port = 3000;
3. Variables para guardar el estado
let page1 = { x: 0, y: 0, width: 100, height: 100 };let page2 = { x: 0, y: 0, width: 100, height: 100 };
4. Sirviendo los archivos del cliente
// Sirve archivos estáticos (HTML, CSS, JS del cliente) desde la carpeta 'views'app.use(express.static(path.join(__dirname, 'views')));
5. Rutas: ¿Qué enviar cuando se pide una URL?
// Define la ruta para servir page1.htmlapp.get('/page1', (req, res) => { res.sendFile(path.join(__dirname, 'views', 'page1.html'));});
// Define la ruta para servir page2.htmlapp.get('/page2', (req, res) => { res.sendFile(path.join(__dirname, 'views', 'page2.html'));});
6. ¡La Magia de Socket.IO! La Conexión
// Evento principal de Socket.IO: se dispara cuando un cliente se conectaio.on('connection', (socket) => { console.log('A user connected - ID:', socket.id); // Muestra el ID único del cliente conectado
// (Aquí dentro irán los manejadores de eventos para ESE cliente)
// Evento: se dispara cuando ESE cliente se desconecta socket.on('disconnect', () => { console.log('User disconnected - ID:', socket.id); });});
7. Escuchando Mensajes de los Clientes
// Dentro de io.on('connection', (socket) => { ... });
// Evento: escucha mensajes 'win1update' enviados desde page1.js socket.on('win1update', (window1, sendid) => { console.log('Received win1update from ID:', socket.id, 'Data:', window1); // Log para ver qué llega page1 = window1; // Actualiza la información del estado de page1 en el servidor
// Envía la información actualizada de page1 a TODOS los demás clientes conectados socket.broadcast.emit('getdata', page1); });
// Evento: escucha mensajes 'win2update' enviados desde page2.js socket.on('win2update', (window2, sendid) => { console.log('Received win2update from ID:', socket.id, 'Data:', window2); // Log para ver qué llega page2 = window2; // Actualiza la información del estado de page2 en el servidor
// Envía la información actualizada de page2 a TODOS los demás clientes conectados socket.broadcast.emit('getdata', page2); });
8. Poner en marcha el Servidor
// Inicia el servidor y lo pone a escuchar en el puerto especificadoserver.listen(port, () => { console.log(`Server is listening on http://localhost:${port}`);});
🚀 Tu solución:
** Análisis del Servidor (server.js)**
1. Dependencias y módulos
- Ventajas de los módulos: Evitan reinventar la rueda. Ejemplo:
express
simplifica rutas HTTP,socket.io
maneja WebSockets por nosotros. - Reflexión: Escribir todo desde cero sería como construir un auto sin piezas prefabricadas: posible, pero ineficiente.
2. Creación del servidor
app = express()
: Es el “cerebro” del servidor por así decirlo.io = socketIO(server)
: Añade superpoderes de comunicación en tiempo real.- Experimento: Cambiar
'views'
por'archivos_cliente'
(inexistente) → Error 404. Confirmé que Express sirve archivos solo desde la carpeta especificada.
3. Estado global
page1
ypage2
guardan posiciones. Importante: El servidor es el “árbitro” que comparte datos entre clientes.
4. Rutas HTTP
- Prueba: Cambiar
/page1
a/pagina_uno
→ La URL debe coincidir exactamente con lo definido enapp.get()
. - *LO que se traduce en: Las rutas son como “direcciones postales”: si las cambias, el servidor no encuentra la “casa”.
5. Conexiones Socket.IO
- Logs de conexión:
- Al abrir
page1
:A user connected - ID: abc123
. - Al abrir
page2
: ID diferente (def456
). - Al cerrar
page1
:User disconnected - ID: abc123
.
- Al abrir
- Key insight: Cada pestaña tiene un ID único, incluso en el mismo navegador.
6. Mensajes entre clientes
- Flujo:
page1
envíawin1update
→ Servidor actualizapage1
.- Servidor usa
broadcast.emit('getdata')
→ Notifica a todos menos al emisor.
- Experimento clave:
- Cambiar
broadcast.emit
asocket.emit
→ Solo el emisor recibe el mensaje (page2
no se actualiza). - Aprendizaje:
broadcast
es esencial para comunicación grupal.
- Cambiar
7. Puerto del servidor
- Cambiar
port
a3001
→ Servidor escucha enhttp://localhost:3001
. - Error común: Olvidar actualizar la URL en el navegador. ¡El puerto en
listen()
y la URL deben coincidir!
confundido con broadcast
Actividad 04
Explorando los clientes (p5.js + Socket.IO)
1. Variables globales y conexión inicial
let currentPageData = { /* ... obtiene datos de la ventana ... */ };let remotePageData = { x: 0, y: 0, width: 100, height: 100 };let point2 = [currentPageData.width / 2, currentPageData.height / 2];
// Conexión al servidor Socket.IO que corre en localhost:3000let socket = io('http://localhost:3000');
2. Manejando la conexión establecida
// Evento: se dispara cuando la conexión con el servidor se establecesocket.on('connect', () => { console.log('Connected to server - My ID:', socket.id); // Envía el estado inicial de esta ventana al servidor socket.emit('win2update', currentPageData, socket.id);});
3. Recibiendo datos del servidor
// Evento: se dispara cuando se recibe un mensaje 'getdata' del servidorsocket.on('getdata', (dataWindow) => { // Actualiza los datos de la ventana remota con la información recibida remotePageData = dataWindow; console.log('Page 2 received data about the other window:', remotePageData);});
4. Detectando cambios y enviando actualizaciones
let previousPageData = { /* ... inicializado igual que currentPageData ... */ };
function checkWindowPosition() { currentPageData = { /* ... obtiene datos actuales de la ventana ... */ };
// Compara el estado actual con el anterior if (currentPageData.x !== previousPageData.x || currentPageData.y !== previousPageData.y || currentPageData.width !== previousPageData.width || currentPageData.height !== previousPageData.height) {
console.log("Page 2 detected change, sending update:", currentPageData); // Log para saber cuándo enviamos point2 = [currentPageData.width / 2, currentPageData.height / 2]; // Si hubo cambios, envía el nuevo estado al servidor socket.emit('win2update', currentPageData, socket.id); previousPageData = currentPageData; // Actualiza el estado anterior para la próxima comparación }}
function draw() { // ... (código de dibujo) ... checkWindowPosition(); // Llama a la función en cada frame // ... (más código de dibujo usando remotePageData) ...}
5. La visualización (draw)
function draw() { background(220); drawCircle(point2[0], point2[1]); // Círculo en el centro de esta ventana checkWindowPosition();
// Calcula la posición relativa de la otra ventana let vector1 = createVector(currentPageData.x, currentPageData.y); let vector2 = createVector(remotePageData.x, remotePageData.y); let resultingVector = createVector(vector2.x - vector1.x, vector2.y - vector1.y);
stroke(50); strokeWeight(20); // Dibuja dónde estaría el centro de la otra ventana RELATIVO a esta drawCircle(resultingVector.x + remotePageData.width / 2, resultingVector.y + remotePageData.height / 2); // Línea conectando los centros (uno local, otro relativo) line(point2[0], point2[1], resultingVector.x + remotePageData.width / 2, resultingVector.y + remotePageData.height / 2);}
function drawCircle(x, y) { /* ... dibuja el círculo ... */ }
🚀 Tu solución:
Solución Actividad 4 **
1. Conexión con el servidor
-
Error sin servidor:
Ventana de terminal GET http://localhost:3000/socket.io/?EIO=4... net::ERR_CONNECTION_REFUSEDSignificado: El cliente intentó conectarse, pero el servidor estaba apagado. ¡Como llamar a un teléfono desconectado!
-
Con servidor activo:
Ventana de terminal Connected to server - My ID: pepapig12gracias a l visto antes, puedo confirmar que Socket.IO necesita el servidor para funcionar.
2. Estado inicial omitido
- Prueba: Comenté
socket.emit('win2update')
al conectar. - Resultado:
page1
mostrópage2
en la posición (0, 0) (valores por defecto).- Solo al mover
page2
,page1
actualizó la vista.
- hace:
Enviar el estado inicial evita “ventanas fantasmas”. Sin él, los demás clientes ven datos incorrectos hasta el primer movimiento.
3. Recepción de datos (getdata
)
- Al mover
page1
:Ventana de terminal Page 2 received data: {x: 200, y: 150, width: 800, height: 600}page2
actualizóremotePageData
y dibujó el círculo remoto en la posición correcta.
- Al mover
page2
:- No hubo logs en
page2
, porquegetdata
solo llega cuando otro cliente envía datos.
- No hubo logs en
4. Detección de cambios inteligente
- Log:
Ventana de terminal Page 2 detected change, sending update: {x: 300, y: 200, ...}- Solo apareció al mover/resizar la ventana, no en cada frame.
- ¿Qué es lo que lo hace eficiente?:
Evita spam de mensajes. Comparar el estado actual con el anterior (
previousPageData
) ahorra ancho de banda.
5. Experimentos visuales
-
Fondo como “radar”:
let distancia = resultingVector.mag();background(map(distancia, 0, 1000, 255, 0)); // Fondo más oscuro = ventanas más lejanasResultado: un efecto como de profundidad
-
Círculo que crece:
let tamaño = remotePageData.width / 4;drawCircle(point2[0], point2[1], tamaño);Pra tener en cuenta: Si
page1
se maximiza, el círculo enpage2
se agranda. -
Línea con color direccional:
stroke(resultingVector.x < 0 ? color(255, 0, 0) : color(0, 0, 255)); // Rojo si está a la izquierda, azul si a la derechaFeedback visual: Identificación rápida de la posición relativa.
Errores comunes y soluciones
Error | Causa | Solución |
---|---|---|
Círculo remoto en (0,0) | Falta emitir estado inicial | Asegurar socket.emit() en connect |
Línea mal dibujada | resultingVector no actualizado | Verificar cálculo con remotePageData |
Diagrama de flujo
sequenceDiagram participant page2 participant Servidor participant page1
page2->>Servidor: connect (ID: pepapig12) page2->>Servidor: win2update (estado inicial) Servidor->>page1: getdata (info de page2) page1->>Servidor: win1update (al moverse) Servidor->>page2: getdata (info de page1)
- Socket.IO actúa como un “mensajero” entre ventanas.
- p5.js usa los datos para dibujar en tiempo real.
- Optimización: Enviar solo cambios reales hace la app más eficiente.
Aplicación 🛠
Actividad 05
Modificación creativa del caso de estudio
👣 Pasos:
🚀 Tu solución:
const express = require('express');const http = require('http');const socketIO = require('socket.io');const app = express();const server = http.createServer(app);const io = socketIO(server);const port = 3000;
let ultimoRitmoBase = null;
// Rutas directas que sirven HTML con p5.js + socket.io + lógica embebidaapp.get('/page1', (req, res) => { res.send(`<!DOCTYPE html><html><head> <title>Page 1 - Ritmo base</title> <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.min.js"></script></head><body> <h1>Page 1 - Marca el ritmo</h1> <p>Pulsa cualquier tecla para marcar el ritmo base.</p> <script> const socket = io(); let lastTimestamp = 0; let circles = [];
function setup() { createCanvas(windowWidth, windowHeight); background(30); }
function draw() { background(30, 30, 30, 80); for (let c of circles) { fill(0, 200, 255, 100); ellipse(c.x, c.y, c.r * 2); c.r += 2; } }
function keyPressed() { const timestamp = Date.now(); if (timestamp - lastTimestamp > 300) { lastTimestamp = timestamp; socket.emit('ritmoBase', { timestamp: timestamp }); circles.push({ x: random(width), y: random(height), r: 20 }); } } </script></body></html> `);});
app.get('/page2', (req, res) => { res.send(`<!DOCTYPE html><html><head> <title>Page 2 - Responde al ritmo</title> <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.min.js"></script></head><body> <h1>Page 2 - Responde al ritmo</h1> <p>Intenta pulsar una tecla justo después de ver el ritmo base.</p> <div id="resultado"></div> <script> const socket = io(); let lastBase = 0; let circles = [];
socket.on('ritmoBase', (data) => { lastBase = data.timestamp; circles.push({ x: random(width), y: random(height), r: 10, color: [255, 100, 100, 100] }); });
socket.on('resultadoRitmo', (data) => { document.getElementById("resultado").innerText = data.sincronizado ? "✅ ¡Bien sincronizado! Desfase: " + data.desfase + "ms" : "⚠️ Fuera de ritmo. Desfase: " + data.desfase + "ms"; });
function setup() { createCanvas(windowWidth, windowHeight); background(0); }
function draw() { background(0, 0, 0, 80); for (let c of circles) { fill(c.color); ellipse(c.x, c.y, c.r * 2); c.r += 2; } }
function keyPressed() { const now = Date.now(); if (now - lastBase > 100) { socket.emit('ritmoRespuesta', { timestamp: now }); } } </script></body></html> `);});
// Socket.io para sincronizar ritmo base y respuestaio.on('connection', (socket) => { console.log('✅ Usuario conectado - ID:', socket.id);
socket.on('disconnect', () => { console.log('❌ Usuario desconectado - ID:', socket.id); });
socket.on('ritmoBase', (data) => { console.log(`🎵 Ritmo base recibido: ${data.timestamp}`); ultimoRitmoBase = data.timestamp; socket.broadcast.emit('ritmoBase', data); });
socket.on('ritmoRespuesta', (data) => { if (ultimoRitmoBase) { const desfase = Math.abs(data.timestamp - ultimoRitmoBase); const sincronizado = desfase <= 200; console.log(`🎯 Ritmo respuesta con desfase: ${desfase}ms`); io.emit('resultadoRitmo', { desfase, sincronizado }); } });});
server.listen(port, () => { console.log(`🚀 Servidor en http://localhost:${port}`);});
http://localhost:3000/page1 para marcar ritmo
http://localhost:3000/page2 para responder y ver arte generativo sincronizado.
Consolidación y metacognición 🤔
Actividad 06
Consolidación de lo aprendido
🚀 Tu solución:
const express = require('express');const http = require('http');const socketIO = require('socket.io');const app = express();const server = http.createServer(app);const io = socketIO(server);const port = 3000;
let ultimoRitmoBase = null;
// Rutas directas que sirven HTML con p5.js + socket.io + lógica embebidaapp.get('/page1', (req, res) => { res.send(`<!DOCTYPE html><html><head> <title>Page 1 - Ritmo base</title> <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.min.js"></script></head><body> <h1>Page 1 - Marca el ritmo</h1> <p>Pulsa cualquier tecla para marcar el ritmo base.</p> <script> const socket = io(); let lastTimestamp = 0; let circles = [];
function setup() { createCanvas(windowWidth, windowHeight); background(30); }
function draw() { background(30, 30, 30, 80); for (let c of circles) { fill(0, 200, 255, 100); ellipse(c.x, c.y, c.r * 2); c.r += 2; } }
function keyPressed() { const timestamp = Date.now(); if (timestamp - lastTimestamp > 300) { lastTimestamp = timestamp; socket.emit('ritmoBase', { timestamp: timestamp }); circles.push({ x: random(width), y: random(height), r: 20 }); } } </script></body></html> `);});
app.get('/page2', (req, res) => { res.send(`<!DOCTYPE html><html><head> <title>Page 2 - Responde al ritmo</title> <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.min.js"></script></head><body> <h1>Page 2 - Responde al ritmo</h1> <p>Intenta pulsar una tecla justo después de ver el ritmo base.</p> <div id="resultado"></div> <script> const socket = io(); let lastBase = 0; let circles = [];
socket.on('ritmoBase', (data) => { lastBase = data.timestamp; circles.push({ x: random(width), y: random(height), r: 10, color: [255, 100, 100, 100] }); });
socket.on('resultadoRitmo', (data) => { document.getElementById("resultado").innerText = data.sincronizado ? "✅ ¡Bien sincronizado! Desfase: " + data.desfase + "ms" : "⚠️ Fuera de ritmo. Desfase: " + data.desfase + "ms"; });
function setup() { createCanvas(windowWidth, windowHeight); background(0); }
function draw() { background(0, 0, 0, 80); for (let c of circles) { fill(c.color); ellipse(c.x, c.y, c.r * 2); c.r += 2; } }
function keyPressed() { const now = Date.now(); if (now - lastBase > 100) { socket.emit('ritmoRespuesta', { timestamp: now }); } } </script></body></html> `);});
// Socket.io para sincronizar ritmo base y respuestaio.on('connection', (socket) => { console.log('✅ Usuario conectado - ID:', socket.id);
socket.on('disconnect', () => { console.log('❌ Usuario desconectado - ID:', socket.id); });
socket.on('ritmoBase', (data) => { console.log(`🎵 Ritmo base recibido: ${data.timestamp}`); ultimoRitmoBase = data.timestamp; socket.broadcast.emit('ritmoBase', data); });
socket.on('ritmoRespuesta', (data) => { if (ultimoRitmoBase) { const desfase = Math.abs(data.timestamp - ultimoRitmoBase); const sincronizado = desfase <= 200; console.log(`🎯 Ritmo respuesta con desfase: ${desfase}ms`); io.emit('resultadoRitmo', { desfase, sincronizado }); } });});
server.listen(port, () => { console.log(`🚀 Servidor en http://localhost:${port}`);});
1. Rol del servidor Node.js El server es el punto de encuentro: recibe mensajes de cada navegador y los reenvía. Sin él, los clientes no “ven” al otro porque, por seguridad, los navegadores no se conectan directo entre sí.
2. socket.emit()
vs. socket.broadcast.emit()
socket.emit()
→ envía a todos, incluido quien mandó.socket.broadcast.emit()
→ envía a todos menos al que mandó. Uso el primero para actualizaciones globales; el segundo cuando el emisor ya sabe la info y solo hace falta avisar a los demás.
3. Socket.IO vs. comunicación serial
- Socket.IO: ideal web-a-web; ventaja → mensaje estructurado y bidireccional en tiempo real; desventaja → necesita servidor y conexión Internet/LAN.
- Serial (ASCII/binario): perfecto para microcontroladores; ventaja → simple, sin red; desventaja → solo punto-a-punto y limitado en distancia.
4. HTTP vs. WebSockets (Socket.IO) HTTP sirve los archivos iniciales (HTML, JS, p5). WebSockets toma el relevo para la charla en tiempo real sin recargar página.
5. Lo que más me sorprendió
Que con pocas líneas (io.on
, socket.emit
) puedo hacer que dos pantallas separadas reaccionen instantáneamente; sentir esa “magia” de interacción en tiempo real fue brutal.
Actividad 07
Autoevaluación
🚀 Tu solución:
Autoevaluación – Unidad Comunicación en Red con Node.js y p5.js
Nivel de confianza (escala 1 a 5):
- Instalar dependencias con
npm install
: 5 - Ejecutar servidor con
npm start
: 5 - Entender estructura básica de servidor Express con Socket.IO: 4
- Conectar un sketch de p5.js al servidor Socket.IO: 4
- Enviar datos al servidor con
socket.emit()
: 5 - Recibir datos con
socket.on()
en cliente: 5 - Entender y aplicar
broadcast
para mensajes a otros clientes: 4 - Depurar con
console.log
: 5 - Modificar el caso de estudio para crear algo nuevo: 5
Estrategia de aprendizaje más útil: Experimentar modificando el código me ayudó a entender qué hacía cada parte y cómo se conectaban los mensajes entre servidor y cliente.
Lo más desafiante:
Entender cuándo y por qué usar broadcast
en lugar de emit
, y cómo evitar que el emisor se afecte a sí mismo. También, sincronizar el tiempo entre dos clientes en tiempo real fue un reto interesante.
Actividad 08
Feedback
🚀 Tu solución:
Comentarios sobre la unidad – Comunicación en red
Lo que más me gustó o fue útil: Poder ver cómo se conectan dos sketches de p5.js en tiempo real fue muy bacano. Ver esa sincronía en acción, especialmente al modificarlo para algo artístico, me ayudó a entender de forma práctica la comunicación en red.
Lo que me pareció confuso o difícil:
Al principio fue un poco enredado entender cómo funciona socket.emit()
versus socket.broadcast.emit()
, sobre todo porque no sabía bien quién recibía qué.
Sugerencias para mejorar: Sería útil tener un esquema visual o diagrama que muestre claramente cómo fluye la información entre cliente-servidor-cliente. También, quizás ejemplos más cortos y directos al principio antes de pasar al proyecto completo.
Ritmo de la unidad: El ritmo me pareció bien. Alcancé a experimentar, equivocarme y entender.