Pantallazo de nucleartweets.com
nucleartweets.com

Cómo se hizo nucleartweets.com

el guido

--

Hola, qué tal?

Vengo a darte la chapa sobre algunos detalles chulos de como montamos este proyecto artístico que simula un contador Geiger. En nuestro contador, en lugar de medir “cuentas por minuto” de partículas ionizantes radioactivas, contamos el número de tuits que contienen la palabra “nuclear” por minuto.

Los clicks

Lo primero que hicimos fue ver si podíamos simular el sonido de un contador Geiger real disparando cada “click” individualmente. La verdad es que podríamos haber cogido el sonido de un contador real y ponerlo de fondo, pero nos hacía ilusión ver si podíamos recrear la cadencia tan característica de estos contadores.

En los contadores reales cada click corresponde con una partícula que impacta en el detector. Resulta que como con tantas cosas en la naturaleza, la cadencia entre clicks sigue una distribución de Poisson.

Función de probabilidad de una distribución de Poisson
Función de probabilidad de una distribución de Poisson

Lambda representa la “tasa” de eventos, que en nuestro caso es el número de cuentas por minuto. Esto nos viene bastante bien. Podemos crear un programa que, a cada intervalo fijo, hace click (o no) con cierta probabilidad en base al número de cuentas por minuto. Por suerte, podemos calcular la probabilidad de que en un intervalo fijo de tiempo se produzca un “click” tomando la inversa de Poisson. Se conoce como “distribución exponencial”.

Función de probabilidad exponencial
Función de probabilidad exponencial

Armados con esta fórmula, podemos crear un programita sencillo que a cada centisegundo evalúa la probabilidad de que se oiga un click según la tasa de tuits del momento. Puedes copiar y pegar este código de ejemplo en la consola JavaScript de tu navegador y probar distintos valores para la variable “tasa”. Con una tasa de sesenta cuentas por minuto, deberías ver una media de un “Click!” impreso por segundo. Prueba a ver:

setInterval(function () {
var tasa = 60;
var tasaNormalizada = tasa / 60 / 100;
var probabilidad = tasaNormalizada * Math.exp(-tasaNormalizada);

if (Math.random() < probabilidad) {
console.log("Click!");
}
}, 10);

NOTA: Un centisegundo es lo mismo que decir “diez milisegundos”, pero sabía que ibas a pensar “este tío es tonto” y me ha hecho bastante gracia imaginármelo.

Los lectores de código ávidos se habrán percatado de que hay mucho birlibirloque en esa línea que inicializa la variable “tasaNormalizada”. La razón por la que hay que “normalizar” la tasa es porque estamos evaluando esta función cada diez milisegundos, y queremos “reducir a escala” la tasa de la distribución exponencial para que de un resultado interesante. Al fin y al cabo, la función exponencial pierde su gracejo si la tasa es muy grande en comparación con el intervalo.

Esta solución trae algunas limitaciones:

  1. La tasa máxima es un “click” cada diez milisegundos o, lo que es lo mismo, seis mil cuentas por minuto. Ya es bastante, ya.
  2. Esto de evaluar la función cada diez milisegundos se presta a que los retrasos de tu navegador a la hora de invocar la rutina se acumulen. Yo he notado carencias en la cadencia (jeje) de diez a veinte cuentas por minuto. Cuanto más viejo sea el trasto con el que visites la página, peor.

Los datos

Hemos creado una ETL que extrae el número de tuits que contienen la palabra “nuclear” cada cinco minutos de la API V2 de Twitter. Y cuando digo ETL, me refiero a una Raspberry Pi 2 que cuelga precariamente de un cable Ethernet conectado al router detrás de la tele. El sistema se mantiene vivo por la gracia de la pestañita de plástico que impide que el conector se desprenda. Una guarrada.

Nuestra heroica Raspberry Pi 🙏

La verdad es que lo podría mover todo a AWS Lambda pero me da una pereza tremenda. Además, el señor Musk ha dicho que se va a cargar la API de Twitter de todas maneras. Ya me dirás tú cuál puede ser el retorno de esa inversión.

Los datos extraídos se meten en una instancia Amazon DynamoDB. Por qué Amazon DynamoDB me preguntas? Pues por que es lo más barato que hay. Hala, ya lo he dicho. Este es el esquema de la tabla por si tienes curiosidad:

topic           | timestamp   | count
----------------+-------------+-------
nuclear-global | 1669957380 | 31
nuclear-global | 1669957440 | 33
nuclear-global | 1669957500 | 42

En DynamoDB, hay un concepto de clave de partición y otro de clave de órden (estoy pasándolo mal intentando evitar anglicismos; “partition key” y “sort key”). En este caso, la partición es por “topic” y se ordena por “timestamp”.

Como ves la idea era tener varios tópicos. Estos iban a tener dos usos:

  1. Probar mejoras en el filtrado de tuits usando la Twitter API V2 de queries. Esto nos iba a permitir mejorar el criterio para contar un tuit como “nuclear”. Por ejemplo, excluyendo ciertos términos, tuits que contengan “nucleartweets.com”, RTs, etc.
  2. Empezar a contar por país or región, cosa que ya habíamos diseñado.

No creo que haya mucho más que contar de este tema pero si tienes preguntas píllame por tuiter y hablamos.

Leer los datos

Esto sí que está más guapo.

En la sección anterior vimos que los datos están en una base de datos DynamoDB en AWS. El problema es el siguiente: cómo puede la página consultar esos datos sin que queden las credenciales expuestas?

Pues con Amazon Cognito. Resulta que este servicio de AWS se puede configurar para que genere credenciales anónimas. Es decir, sin que te autentiques, se te dan unas claves de una remesa al visitar la página. La remesa (“identity pool”) la configuramos para que solo se pudiera leer de la tabla en cuestión. También hicimos que las credenciales sólo duraran un tiempo y pudiesen consumir sólo cierta cuota.

Esto no va a evitar que alguien abuse de la cuota si quiere, pero por lo menos el daño está controlado y el esfuerzo de configurarlo es mínimo. Un mini-punto para Amazon.

El frontend

Está montado con un Webpack (“tú si que eres un web paquete”). Webpack, cosa que no sabía antes de este proyecto, es algo que te permite usar cualquier librería NPM en tu navegador. Hace otras cosas, pero esa es la razón por la que lo usamos nosotros: poder usar las librerías de AWS desde la página.

Para lograrlo, Webpack coge todo el código de todas las librerías que has usado y las inyecta como un mega-churro de texto minificado en tu página. Literal. No esperes algo más glamuroso que eso porque no lo hay.

También compila código LESS a CSS, minifica el resultado, gestiona tus assets, etc. Lo usaré de aquí en adelante.

Conclusión

Quiero agradecer a mi compa Carlos Sánchez por crear este proyecto conmigo y a todos los que habéis entrado a visitar la página. Ya lo mencionamos, pero la motivación para este proyecto vino del libro y película “On the beach”. Merece la pena leer el libro o ver la peli (sale Ava Gardner ❤).

No veáis el remake del 2000 que no hay por donde cogerlo.

Hasta luego!

--

--