Saltearse al contenido

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.
    image

image

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, el Servidor 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.
  • /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 el CSS 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 del HTML, el navegador pausa la construcción, ejecuta el JS y luego sigue.
  • Después de cargar el HTML: a menudo, los scripts se colocan al final del <body> o se marcan con atributos como defer o async para que se ejecuten después de que la estructura principal (DOM) esté lista. ¡Esto es importante para que el JS pueda encontrar y manipular los elementos HTML!

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 ejecuta otraFuncion()”.

“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 y JS 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).
  • 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)).

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
server.js
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.html
app.get('/page1', (req, res) => {
res.sendFile(path.join(__dirname, 'views', 'page1.html'));
});
// Define la ruta para servir page2.html
app.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 conecta
io.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 especificado
server.listen(port, () => {
console.log(`Server is listening on http://localhost:${port}`);
});

🚀 Tu solución:

** Análisis del Servidor (server.js)**

image

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 y page2 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 en app.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.
  • Key insight: Cada pestaña tiene un ID único, incluso en el mismo navegador.

6. Mensajes entre clientes

  • Flujo:
    1. page1 envía win1update → Servidor actualiza page1.
    2. Servidor usa broadcast.emit('getdata') → Notifica a todos menos al emisor.
  • Experimento clave:
    • Cambiar broadcast.emit a socket.emit → Solo el emisor recibe el mensaje (page2 no se actualiza).
    • Aprendizaje: broadcast es esencial para comunicación grupal.

7. Puerto del servidor

  • Cambiar port a 3001 → Servidor escucha en http://localhost:3001.
  • Error común: Olvidar actualizar la URL en el navegador. ¡El puerto en listen() y la URL deben coincidir!

confundido con broadcast image

Actividad 04

Explorando los clientes (p5.js + Socket.IO)

1. Variables globales y conexión inicial
page2.js
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:3000
let socket = io('http://localhost:3000');
2. Manejando la conexión establecida
// Evento: se dispara cuando la conexión con el servidor se establece
socket.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 servidor
socket.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_REFUSED

    Significado: 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: pepapig12

    gracias 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, porque getdata solo llega cuando otro cliente envía datos.

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

  1. Fondo como “radar”:

    let distancia = resultingVector.mag();
    background(map(distancia, 0, 1000, 255, 0)); // Fondo más oscuro = ventanas más lejanas

    Resultado: un efecto como de profundidad

  2. 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 en page2 se agranda.

  3. 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 derecha

    Feedback visual: Identificación rápida de la posición relativa.


Errores comunes y soluciones

ErrorCausaSolución
Círculo remoto en (0,0)Falta emitir estado inicialAsegurar socket.emit() en connect
Línea mal dibujadaresultingVector no actualizadoVerificar 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 embebida
app.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 respuesta
io.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 embebida
app.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 respuesta
io.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}`);
});

image

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.