<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator>
  <link href="http://blog.devcito.org/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="http://blog.devcito.org/" rel="alternate" type="text/html"/>
  <updated>2026-05-24T21:21:01+00:00</updated>
  <id>http://blog.devcito.org/</id>
  <title type="html">devcito blog</title>
  
    <subtitle>El blog de devcito</subtitle>
  
  
    <entry>
      <title type="html">Creación del Blog y Nuevo Diseño Git Sketchbook</title>
      <link href="http://blog.devcito.org/creacion-del-blog/" rel="alternate" type="text/html" title="Creación del Blog y Nuevo Diseño Git Sketchbook"/>
      <published>2026-05-24T00:00:00+00:00</published>
      <updated>2026-05-24T00:00:00+00:00</updated>
      <id>http://blog.devcito.org/creacion-del-blog</id>
      <content type="html" xml:base="http://blog.devcito.org/creacion-del-blog/">&lt;p&gt;¡Hola a todos! Este es el primer artículo oficial de &lt;strong&gt;devcito blog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;He creado este blog con un propósito muy claro: &lt;strong&gt;dar visibilidad a las cosas que hago y desarrollo en mis proyectos personales&lt;/strong&gt;. Para mí, es un espacio donde documentar mis avances, registrar soluciones a problemas reales de programación y mostrar los experimentos y proyectos que voy construyendo en mi día a día.&lt;/p&gt;

&lt;h2 id=&quot;el-diseño-git-sketchbook&quot;&gt;El Diseño: Git Sketchbook&lt;/h2&gt;

&lt;p&gt;Para este espacio quería un diseño limpio, que facilitara la lectura de código pero que mantuviera la esencia de una consola o entorno de desarrollo. Este diseño incluye:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Margen de Números de Línea:&lt;/strong&gt; Un script dinámico calcula la altura de la página y renderiza números de línea en el lateral izquierdo, emulando un editor de código.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cabecera Git:&lt;/strong&gt; Muestra la ruta del proyecto (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcito / blog&lt;/code&gt;) y la rama activa actual (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fondo de Cuadrícula:&lt;/strong&gt; Un patrón de cuadrícula de fondo que simula una libreta física de bocetos para desarrolladores.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;qué-verás-por-aquí&quot;&gt;¿Qué verás por aquí?&lt;/h2&gt;

&lt;p&gt;Principalmente compartiré el progreso y la arquitectura de mis proyectos personales, retos superados en CSS/JS, configuraciones de terminal y desarrollo de software práctico.&lt;/p&gt;

&lt;p&gt;¡Gracias por pasar y seguir el progreso!&lt;/p&gt;
</content>
      
        <category term="blog"/>
      
        <category term="git"/>
      
        <category term="css"/>
      
        <category term="webdev"/>
      
    </entry>
  
    <entry>
      <title type="html">Cómo funciona el algoritmo de colores de Armonic Themes</title>
      <link href="http://blog.devcito.org/el-algoritmo-de-colores-de-armonic-themes/" rel="alternate" type="text/html" title="Cómo funciona el algoritmo de colores de Armonic Themes"/>
      <published>2026-05-09T00:00:00+00:00</published>
      <updated>2026-05-09T00:00:00+00:00</updated>
      <id>http://blog.devcito.org/el-algoritmo-de-colores-de-armonic-themes</id>
      <content type="html" xml:base="http://blog.devcito.org/el-algoritmo-de-colores-de-armonic-themes/">&lt;p&gt;Una mirada técnica a cómo generar paletas que siempre se ven bien, en claro y en oscuro.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/7f80a2ffab01e342526ae1bad11a5559.png&quot; alt=&quot;7f80a2ffab01e342526ae1bad11a5559.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;el-problema-de-las-paletas-aleatorias&quot;&gt;El problema de las paletas “aleatorias”&lt;/h2&gt;

&lt;p&gt;Generar colores al azar es fácil. Generar colores al azar que se vean bien juntos es otro asunto completamente diferente.&lt;/p&gt;

&lt;p&gt;La mayoría de los generadores de temas eligen cada color de forma independiente: un rojo por aquí, un azul por allá, un verde que nadie pidió. El resultado suele ser una paleta caótica donde los colores pelean entre sí en lugar de complementarse.&lt;/p&gt;

&lt;p&gt;Armonic Themes resuelve esto con un principio fundamentalmente distinto.&lt;/p&gt;

&lt;h2 id=&quot;el-principio-fundamental-un-solo-hue-lo-gobierna-todo&quot;&gt;El principio fundamental: un solo hue lo gobierna todo&lt;/h2&gt;

&lt;p&gt;En lugar de elegir cada color de manera independiente, el algoritmo parte de un único Hue base (un ángulo en la rueda de color, entre 0° y 360°) y deriva todos los demás colores a partir de él usando relaciones matemáticas establecidas.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Hue base aleatorio (0–360°)
        │
        ▼
┌─────────────────────┐
│ Estrategia de       │  → define secondaryHue y accentHue
│ armonía (al azar)   │
└─────────────────────┘
        │
        ▼
Todos los colores de la paleta
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esto garantiza que, sin importar qué número salga, los colores siempre tendrán una relación armónica entre sí.&lt;/p&gt;

&lt;h2 id=&quot;las-estrategias-de-armonía&quot;&gt;Las estrategias de armonía&lt;/h2&gt;

&lt;p&gt;El algoritmo incluye 9 estrategias de armonía, elegidas al azar en cada generación:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Análoga&lt;/strong&gt;: Hues vecinos (±18°–55°) → Suave, elegante, “de diseñador”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Análoga amplia&lt;/strong&gt;: Vecinos más lejanos (±55°–160°) → Sofisticada, con más tensión&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Complementaria&lt;/strong&gt;: Hue opuesto (+180°) → Alto contraste, energética&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Triádica&lt;/strong&gt;: 3 hues a 120° entre sí → Rica, colorida, balanceada&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Split-complementaria&lt;/strong&gt;: Vecinos del opuesto (+150° y +210°) → Contraste sin ser agresiva&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Doble-split&lt;/strong&gt;: Ambos lados del opuesto (±150°) → Vibrante y simétrica&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tetrádica&lt;/strong&gt;: Cuadrado en la rueda (90° y 270°) → Máxima variedad de hues&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Monocromática&lt;/strong&gt;: Mismo hue con mínima variación (±12°–18°) → Elegancia pura, una sola familia&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Caótica&lt;/strong&gt;: Hues completamente libres → Máxima sorpresa, sin reglas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por ejemplo, si el hue base es 220° (azul):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Análoga → secondary ≈ 195°, accent ≈ 245°&lt;/li&gt;
  &lt;li&gt;Complementaria → accent ≈ 40° (naranja)&lt;/li&gt;
  &lt;li&gt;Triádica → secondary ≈ 340° (magenta), accent ≈ 100° (verde)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;los-moods-la-personalidad-de-cada-paleta&quot;&gt;Los Moods: la personalidad de cada paleta&lt;/h2&gt;

&lt;p&gt;La estrategia de armonía controla qué hues se usan. Pero el mood controla la saturación y luminosidad de cada color, que es lo que le da el “carácter” a la paleta.&lt;/p&gt;

&lt;p&gt;El algoritmo define 17 moods, cada uno con rangos precisos de [min, max] para saturación (S) y luminosidad (L):&lt;/p&gt;

&lt;p&gt;`- &lt;strong&gt;bold&lt;/strong&gt; → primaryS: [52-82%], primaryL: [38-60%] — Firme, seguro&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;soft&lt;/strong&gt; → primaryS: [28-58%], primaryL: [52-74%] — Suave, amable&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;vibrant&lt;/strong&gt; → primaryS: [74-100%], primaryL: [44-64%] — Eléctrico, vivo&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;pastel&lt;/strong&gt; → primaryS: [38-72%], primaryL: [68-87%] — Dulce, delicado&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;neon&lt;/strong&gt; → primaryS: [88-100%], primaryL: [52-68%] — Extremo, digital&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;jewel&lt;/strong&gt; → primaryS: [62-92%], primaryL: [22-42%] — Esmeralda, zafiro, rubí&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;mono&lt;/strong&gt; → primaryS: [4-18%],  primaryL: [18-52%] — Casi escala de grises&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;midnight&lt;/strong&gt; → primaryS: [48-82%], primaryL: [48-72%] — Nocturno, intenso&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;corporate&lt;/strong&gt; → primaryS: [22-52%], primaryL: [26-50%] — Profesional, sobrio&lt;/li&gt;
  &lt;li&gt;… y más`&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La combinación de estrategia de armonía + mood produce miles de paletas visualmente coherentes sin intervención manual.&lt;/p&gt;

&lt;h2 id=&quot;la-micro-variación-el-toque-de-aleatoriedad-controlada&quot;&gt;La micro-variación: el toque de aleatoriedad controlada&lt;/h2&gt;

&lt;p&gt;Para que dos paletas con el mismo mood y estrategia no sean idénticas, el algoritmo aplica un multiplicador global de saturación y luminosidad en cada generación:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;satTwist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Amplifica o reduce TODA la saturación&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;litTwist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.92&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Pequeña variación de luminosidad global&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;satTwist&lt;/code&gt; de 1.32 hace que todos los colores sean un 32% más saturados que lo normal para ese mood. Uno de 0.72 los apaga. Esto produce una variedad enorme dentro de la misma “familia” de paletas.&lt;/p&gt;

&lt;h2 id=&quot;accesibilidad-automática-wcag-sin-esfuerzo&quot;&gt;Accesibilidad automática: WCAG sin esfuerzo&lt;/h2&gt;

&lt;p&gt;Uno de los detalles más cuidados del algoritmo es que nunca hay que definir manualmente el color del texto. Cada foreground se calcula automáticamente usando la fórmula de luminancia relativa de las directrices WCAG 2.1:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;Luminancia&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;relativa&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2126&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.7152&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0722&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Si la luminancia del fondo supera 0.179 (umbral perceptual donde el ojo empieza a ver el color como “claro”), el texto se vuelve oscuro. Si está por debajo, se vuelve claro. Ambas variantes tienen un tinte sutil del hue base o secundario para que no sean un gris muerto:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Fondo claro → texto oscuro teñido&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;darkTintHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Fondo oscuro → texto claro teñido  &lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lightTintHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;92&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;98&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;El resultado es que todo texto generado cumple contraste legible de manera automática, y aun así los textos tienen personalidad cromática propia.&lt;/p&gt;

&lt;h2 id=&quot;cómo-funciona-el-modo-oscuro&quot;&gt;Cómo funciona el modo oscuro&lt;/h2&gt;

&lt;p&gt;Aquí está la magia. En lugar de “invertir” los colores o usar un overlay semitransparente (como hacen muchos frameworks), el algoritmo genera ambos modos desde el principio, compartiendo los mismos hues pero con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lightness&lt;/code&gt; completamente diferente.&lt;/p&gt;

&lt;h3 id=&quot;lo-que-se-comparte-entre-light-y-dark&quot;&gt;Lo que se comparte entre light y dark&lt;/h3&gt;

&lt;p&gt;El color &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;primary&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destructive&lt;/code&gt; y &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ring&lt;/code&gt; son idénticos en ambos modos. Son los colores de acción y tienen su propio contraste garantizado.&lt;/p&gt;

&lt;h3 id=&quot;lo-que-cambia-entre-light-y-dark&quot;&gt;Lo que cambia entre light y dark&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;background&lt;/code&gt; → light L: 93–99% (casi blanco), dark L: 2–13% (casi negro)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;muted&lt;/code&gt; → light L: 86–96% (blanco grisáceo), dark L: 11–26% (gris oscuro)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;border&lt;/code&gt; → light L: 75–91% (gris claro), dark L: 23–45% (gris medio oscuro)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secondary&lt;/code&gt; → saturación mínima, L alta / saturación mínima, L baja&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accent&lt;/code&gt; → saturación media, L alta / saturación media, L baja&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y aquí viene el detalle que hace que los modos oscuros tengan personalidad: los fondos oscuros no usan necesariamente el mismo hue que los fondos claros.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Para el fondo oscuro, se elige un darkTintHue con más libertad:&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;darkTintCandidates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;baseHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;secondaryHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;accentHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;260&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;darkTintHue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.35&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;darkTintCandidates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;randInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;darkTintCandidates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;baseHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esto significa que el fondo oscuro puede tener un tinte azul profundo aunque el color primario sea naranja. Es lo que evita que todos los modos oscuros sean “gris neutro con colores encima”.&lt;/p&gt;

&lt;h2 id=&quot;el-flujo-completo-en-12-pasos&quot;&gt;El flujo completo en 12 pasos&lt;/h2&gt;

&lt;p&gt;`1. Elegir HUE BASE (0–360°)&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Elegir ESTRATEGIA DE ARMONÍA → calcular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secondaryHue&lt;/code&gt; y &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accentHue&lt;/code&gt;
2.5 Elegir MOOD → rangos S/L para cada token&lt;/li&gt;
  &lt;li&gt;Aplicar micro-variación (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;satTwist&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;litTwist&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Generar PRIMARY (hue base + rangos del mood)&lt;/li&gt;
  &lt;li&gt;Generar SECONDARY en versión base / light / dark&lt;/li&gt;
  &lt;li&gt;Generar ACCENT en versión light / dark&lt;/li&gt;
  &lt;li&gt;Generar DESTRUCTIVE (siempre en zona de rojos, 330–30°)&lt;/li&gt;
  &lt;li&gt;Elegir &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darkTintHue&lt;/code&gt; y &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lightTintHue&lt;/code&gt; para los fondos&lt;/li&gt;
  &lt;li&gt;Generar BACKGROUND light (L: 93–99%) y dark (L: 2–13%)&lt;/li&gt;
  &lt;li&gt;Generar MUTED light y dark&lt;/li&gt;
  &lt;li&gt;Generar BORDER light y dark&lt;/li&gt;
  &lt;li&gt;Calcular todos los FOREGROUNDS por luminancia WCAG automática`&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;nombrar-cada-paleta&quot;&gt;Nombrar cada paleta&lt;/h2&gt;

&lt;p&gt;Para que cada tema sea memorable, el algoritmo genera nombres combinando dos listas de palabras:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Arctic, Velvet, Neon, Cosmic, Ember, Frosted...] + [Dawn, Wave, Pulse, Storm, Bloom...]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ “Arctic Dawn”, “Velvet Pulse”, “Cosmic Storm”&lt;/p&gt;

&lt;p&gt;Son 70+ adjetivos × 70+ sustantivos = más de 4,900 combinaciones posibles, todas con ese sabor evocador de los nombres de temas de código.&lt;/p&gt;

&lt;h2 id=&quot;en-resumen&quot;&gt;En resumen&lt;/h2&gt;

&lt;p&gt;Lo que hace especial a este algoritmo es que la aleatoriedad está estructurada. Nada se elige de forma verdaderamente libre: el azar actúa dentro de rangos matemáticamente definidos, siguiendo relaciones de color probadas durante décadas de teoría del diseño.&lt;/p&gt;

&lt;p&gt;El resultado es un generador que puede producir miles de paletas distintas, cada una coherente y usable desde el primer intento, con modo claro y oscuro listos para producción.&lt;/p&gt;

&lt;p&gt;Escrito analizando el código fuente de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color-algorithm.js&lt;/code&gt; del proyecto Armonic Themes.&lt;/p&gt;

&lt;p&gt;Puedes verlo en https://themes.devcito.org&lt;/p&gt;
</content>
      
        <category term="colores"/>
      
        <category term="temas"/>
      
        <category term="armonía"/>
      
        <category term="diseño"/>
      
        <category term="algo"/>
      
    </entry>
  
    <entry>
      <title type="html">El algoritmo de Azar: cómo funciona el generador de aleatoriedad</title>
      <link href="http://blog.devcito.org/el-algoritmo-de-azar-como-funciona-el-generador-de-aleatoriedad/" rel="alternate" type="text/html" title="El algoritmo de Azar: cómo funciona el generador de aleatoriedad"/>
      <published>2026-04-26T00:00:00+00:00</published>
      <updated>2026-04-26T00:00:00+00:00</updated>
      <id>http://blog.devcito.org/el-algoritmo-de-azar-como-funciona-el-generador-de-aleatoriedad</id>
      <content type="html" xml:base="http://blog.devcito.org/el-algoritmo-de-azar-como-funciona-el-generador-de-aleatoriedad/">&lt;p&gt;&lt;img src=&quot;../assets/images/cfa64ef13f4a4719126bc8f5715ac9c5.png&quot; alt=&quot;cfa64ef13f4a4719126bc8f5715ac9c5.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Azar es una suite de herramientas de aleatoriedad completamente estática, construida con Astro y Tailwind CSS. Su gran diferencia con otros generadores web es que no depende solo de Math.random(): aprovecha la entropía del usuario, la entropía del navegador y un pool interno que se alimenta de eventos reales.&lt;/p&gt;

&lt;p&gt;Este artículo explica en detalle cómo funciona el algoritmo de Azar, por qué es mejor usarlo en este tipo de herramienta y qué hace cada parte del sistema.&lt;/p&gt;

&lt;h2 id=&quot;1-introducción-por-qué-azar-es-distinto&quot;&gt;1. Introducción: por qué Azar es distinto&lt;/h2&gt;

&lt;p&gt;La mayoría de las implementaciones de aleatoriedad en JavaScript se limitan a una línea:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Eso es suficiente para animaciones simples, pero no para casos donde queremos que el resultado esté influido por la interacción humana. En Azar, toda la lógica corre en el navegador del visitante, y el objetivo es capturar entropía real y mezclarla de forma segura.&lt;/p&gt;

&lt;h3 id=&quot;qué-busca-azar&quot;&gt;¿Qué busca Azar?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;usar el movimiento del cursor como fuente de datos impredecibles&lt;/li&gt;
  &lt;li&gt;capturar toques y movimiento de dispositivo en móviles&lt;/li&gt;
  &lt;li&gt;usar el jitter natural de requestAnimationFrame&lt;/li&gt;
  &lt;li&gt;combinarlo con Math.random() para mayor robustez&lt;/li&gt;
  &lt;li&gt;exponer una API global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.azarRandom()&lt;/code&gt; que todas las herramientas usan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Azar no pretende reemplazar un generador criptográfico de clase mundial, pero sí añade calidad real frente a una solución puramente determinista basada en Math.random().&lt;/p&gt;

&lt;h2 id=&quot;2-arquitectura-general-del-motor&quot;&gt;2. Arquitectura general del motor&lt;/h2&gt;

&lt;p&gt;El núcleo del motor está en &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/layouts/Layout.astro&lt;/code&gt;, y se compone de tres bloques:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;el pool interno de entropía (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Uint32Array&lt;/code&gt; de 256 elementos)&lt;/li&gt;
  &lt;li&gt;la señal visual de entropía &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;azarSignal&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;la API pública &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.azarRandom()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todas las páginas y componentes del sitio usan esta misma API para generar resultados aleatorios.&lt;/p&gt;

&lt;h3 id=&quot;21-estructura-del-pool&quot;&gt;2.1 Estructura del pool&lt;/h3&gt;

&lt;p&gt;Azar usa un buffer circular de 256 enteros sin signo:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Uint32Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Este pool no es solo almacenamiento: es el corazón de la mezcla. Cada evento relevante inyecta datos y cada generación consume valores del pool para producir números.&lt;/p&gt;

&lt;h3 id=&quot;22-función-de-mezcla-xormix&quot;&gt;2.2 Función de mezcla xorMix&lt;/h3&gt;

&lt;p&gt;La función que mezcla valores internamente es un xorshift rápido:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;xorMix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esta transformación no es un generador criptográfico independiente, pero sí es excelente para mezclar bits de forma eficiente.&lt;/p&gt;

&lt;p&gt;Cada vez que se alimenta el pool, se hace así:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;xorMix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ese &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feedPool&lt;/code&gt; combina el valor nuevo con el valor ya existente en la posición actual del pool, aplica la mezcla y avanza el puntero.&lt;/p&gt;

&lt;h2 id=&quot;3-las-fuentes-de-entropía-humana&quot;&gt;3. Las fuentes de entropía humana&lt;/h2&gt;

&lt;p&gt;Azar recoge entropía directa de la interacción del usuario. Estas son las fuentes principales:&lt;/p&gt;

&lt;h3 id=&quot;31-mousemove&quot;&gt;3.1 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mousemove&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Cada movimiento del mouse contribuye con velocidad y coordenadas:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mousemove&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;speed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hypot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;movementX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;movementY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;boostSignal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;speed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0045&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;movementX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;137&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;movementY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;59&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clientX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clientY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;passive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se usa &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movementX&lt;/code&gt; y &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movementY&lt;/code&gt; para capturar la dinámica del movimiento, y además se inyectan las coordenadas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clientX/clientY&lt;/code&gt; en el pool.&lt;/p&gt;

&lt;h3 id=&quot;32-touchmove-y-touchstart&quot;&gt;3.2 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touchmove&lt;/code&gt; y &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touchstart&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;En móviles, los toques son igual de valiosos:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;touchmove&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;touches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;boostSignal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clientX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clientY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;passive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;touchstart&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;boostSignal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.09&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;passive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Los toques agregan coordenadas táctiles y tiempo, lo que es útil cuando no hay mouse.&lt;/p&gt;

&lt;h3 id=&quot;33-click&quot;&gt;3.3 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;click&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Cada clic suma entropía adicional:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;boostSignal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.07&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clientX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clientY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;passive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;La idea es simple: cada interacción del usuario debe cambiar el estado interno del pool.&lt;/p&gt;

&lt;h3 id=&quot;34-devicemotion&quot;&gt;3.4 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devicemotion&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Los móviles con acelerómetro proporcionan datos físicos reales:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DeviceMotionEvent&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;devicemotion&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;acceleration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;boostSignal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;passive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;El movimiento real del dispositivo introduce ruido que difícilmente puede ser predecible desde el lado del servidor.&lt;/p&gt;

&lt;h2 id=&quot;4-entropía-de-fondo-requestanimationframe&quot;&gt;4. Entropía de fondo: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestAnimationFrame&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Incluso cuando el usuario está quieto, hay entropía disponible. El tiempo entre frames no es constante: depende de la CPU, el sistema operativo y otros procesos.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastRaf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rafLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;diff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastRaf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;lastRaf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;diff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;feedPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rafLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rafLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ese &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff&lt;/code&gt; y el &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;now&lt;/code&gt; son mediciones de jitter natural. No son completamente aleatorios, pero sí aportan ruido constante.&lt;/p&gt;

&lt;p&gt;Este patrón está inspirado en técnicas de generación de entropía donde se usa el retardo de reloj o de ciclo para obtener bits impredecibles.&lt;/p&gt;

&lt;h2 id=&quot;5-señal-visual-azarsignal&quot;&gt;5. Señal visual: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;azarSignal&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Azar no solo genera números; también muestra un HUD que indica la “cantidad de entropía activa”. Esa señal es útil para el usuario y para transmitir la idea de que el sistema está vivo.&lt;/p&gt;

&lt;p&gt;El cálculo es este:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FLOOR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CEILING&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DECAY_K&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.975&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;signal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FLOOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boostSignal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;signal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CEILING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;signal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;signal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FLOOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;signal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DECAY_K&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FLOOR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DECAY_K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;En otras palabras:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;hay un piso mínimo de 8%&lt;/li&gt;
  &lt;li&gt;no se llega a 100%&lt;/li&gt;
  &lt;li&gt;cada frame usa un decaimiento multiplicativo&lt;/li&gt;
  &lt;li&gt;la señal sube con movimientos y toques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eso produce una experiencia visual donde las acciones del usuario se traducen en una marca visible de energía aleatoria.&lt;/p&gt;

&lt;h2 id=&quot;6-generación-final-cómo-sale-el-número-aleatorio&quot;&gt;6. Generación final: cómo sale el número aleatorio&lt;/h2&gt;

&lt;p&gt;La función pública es &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;azarRandom()&lt;/code&gt; y es la que usan todas las herramientas del sitio.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azarRandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;xorMix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;poolIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;POOL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;humanEntropy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4294967296&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;machineEntropy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;humanEntropy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;machineEntropy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Paso a paso:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;se extraen 8 valores del pool y se combinan con xor&lt;/li&gt;
  &lt;li&gt;se mezcla con el tiempo actual (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;performance.now()&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;se transforma de nuevo con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xorMix&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;se guarda el valor resultante en el pool&lt;/li&gt;
  &lt;li&gt;se normaliza a un número entre 0 y 1&lt;/li&gt;
  &lt;li&gt;se suma con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt; y se aplica módulo 1&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;por-qué-sumar-mathrandom&quot;&gt;¿Por qué sumar &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt;?&lt;/h3&gt;

&lt;p&gt;Porque aporta una fuente adicional del propio motor JS del navegador. Si por alguna razón la entropía del pool fuera baja, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt; refuerza el resultado.&lt;/p&gt;

&lt;p&gt;La suma modular mantiene la distribución uniforme en el rango [0, 1).&lt;/p&gt;

&lt;h2 id=&quot;7-ventajas-frente-a-mathrandom-solo&quot;&gt;7. Ventajas frente a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt; solo&lt;/h2&gt;

&lt;h3 id=&quot;71-más-variedad-de-fuentes&quot;&gt;7.1 Más variedad de fuentes&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt; depende del motor del navegador. Azar usa además:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;movimiento del mouse&lt;/li&gt;
  &lt;li&gt;toques táctiles&lt;/li&gt;
  &lt;li&gt;acelerómetro&lt;/li&gt;
  &lt;li&gt;jitter de frames&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;72-estado-compartido&quot;&gt;7.2 Estado compartido&lt;/h3&gt;

&lt;p&gt;El pool interno acumula entropía entre eventos. Es decir, cada interacción altera un estado que persiste y se reutiliza.&lt;/p&gt;

&lt;h3 id=&quot;73-más-difícil-de-predecir-desde-fuera&quot;&gt;7.3 Más difícil de predecir desde fuera&lt;/h3&gt;

&lt;p&gt;Un generador que solo usa &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt; puede ser más fácil de inferir si el motor es conocido. Agregar la entropía del usuario hace que el resultado dependa de factores externos al navegador.&lt;/p&gt;

&lt;h2 id=&quot;8-dónde-usa-azar-esta-api&quot;&gt;8. Dónde usa Azar esta API&lt;/h2&gt;

&lt;p&gt;Las herramientas del proyecto no generan números por su cuenta. Todas reclaman el mismo punto de acceso global:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/components/tools/Sorteo.astro&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/components/tools/Dados.astro&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/components/tools/Cartas.astro&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/components/tools/Numero.astro&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/components/tools/Moneda.astro&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/components/tools/Ruleta.astro&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esto asegura consistencia: cualquier mejora en el motor se beneficia a todas las herramientas.&lt;/p&gt;

&lt;h2 id=&quot;9-seo-y-compartición-social&quot;&gt;9. SEO y compartición social&lt;/h2&gt;

&lt;p&gt;Aunque el algoritmo es la parte técnica, Azar también cuida el SEO y la presencia social.&lt;/p&gt;

&lt;p&gt;En &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/layouts/Layout.astro&lt;/code&gt; hay metadatos como:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;canonical dinámico&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;og:image&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twitter:image&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;preload de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/preview.png&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;JSON-LD con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema.org&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esto mejora la calidad del sitio cuando se comparte en redes y también ayuda al rastreo de motores de búsqueda.&lt;/p&gt;

&lt;h3 id=&quot;enlaces-relacionados&quot;&gt;Enlaces relacionados&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt; Tailwind CSS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Math/random&quot;&gt;MDN&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/es/docs/Web/API/Crypto/getRandomValues&quot;&gt;MDN&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crypto.getRandomValues()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://schema.org/WebApplication&quot;&gt;schema.org&lt;/a&gt; WebApplication&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/es/docs/Web/API/window/requestAnimationFrame&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestAnimationFrame&lt;/code&gt;
&lt;/a&gt;
    &lt;h2 id=&quot;10-conclusión&quot;&gt;10. Conclusión&lt;/h2&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Azar demuestra cómo un sitio estático puede ofrecer aleatoriedad de mejor calidad sin backend. En lugar de depender únicamente de un PRNG del navegador, combina:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;entropía humana real,&lt;/li&gt;
  &lt;li&gt;jitter de hardware y sistema,&lt;/li&gt;
  &lt;li&gt;mezcla interna de pool,&lt;/li&gt;
  &lt;li&gt;y el propio &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El resultado es un sistema apropiado para juegos, sorteos y herramientas de azar en el frontend.&lt;/p&gt;

&lt;p&gt;Si quieres, puedo preparar otra versión enfocada en cómo integrar este motor en una librería independiente o cómo agregarlo fácilmente a otro proyecto Astro.&lt;/p&gt;

&lt;p&gt;Puedes verlo en https://azar.devcito.org&lt;/p&gt;
</content>
      
        <category term="azar"/>
      
        <category term="aleatoriedad"/>
      
        <category term="astro"/>
      
        <category term="tailwind"/>
      
        <category term="seguridad"/>
      
        <category term="javascript"/>
      
    </entry>
  
    <entry>
      <title type="html">Google AdSense en Angular con osbo-adsense-angular</title>
      <link href="http://blog.devcito.org/adsense-in-angular/" rel="alternate" type="text/html" title="Google AdSense en Angular con osbo-adsense-angular"/>
      <published>2024-06-30T00:00:00+00:00</published>
      <updated>2024-06-30T00:00:00+00:00</updated>
      <id>http://blog.devcito.org/adsense-in-angular</id>
      <content type="html" xml:base="http://blog.devcito.org/adsense-in-angular/">&lt;p&gt;&lt;strong&gt;osbo-adsense-angular&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Google AdSense for Angular Applications&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code&lt;/strong&gt;
https://github.com/Programmercito/osbo-adsense-angular
Install&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install osbo-adsense-angular&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Add adsense code&lt;/strong&gt;
Use the standard AdSense code somewhere in your &amp;lt;head&amp;gt;&amp;lt;/head&amp;gt; as you &lt;a href=&quot;https://support.google.com/adsense/answer/7477845&quot;&gt;normally would&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;script async src=//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import NgModule&lt;/strong&gt;
Add AdsenseModule to the imports of your NgModule&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import { OsboSenseModule } from &apos;osbo-sense&apos;;
@NgModule({ 
imports: [   
OsboSenseModule   
}),    ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Use in the component html&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;ngosbo-adsense [config]=&quot;config&quot;&amp;gt;&amp;lt;/ngosbo-adsense&amp;gt;
&lt;/code&gt;
In the componente Typescript&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// multiples ads
this.config = {} as SenseConfiguration;
this.config.tipo = &quot;multiple&quot;;
this.config.dataadclient = &quot;ca-pub-xxxxx&quot;;
this.config.dataadformat = &quot;autorelaxed&quot;;   
this.config.dataadslot = &quot;999999999&quot;;   
this.config.style = &quot;display:block&quot;;
 // display ads   
this.config1 = {} as SenseConfiguration;
this.config1.tipo = &quot;display&quot;;   
this.config1.dataadclient = &quot;ca-pub-xxxxx&quot;;   
this.config1.dataadformat = &quot;auto&quot;;   
this.config1.dataadslot = &quot;9999999&quot;;   
this.config1.datafullwidthresponsive = &quot;true&quot;;   
this.config1.style = &quot;display:block&quot;;
// infeed ads    this.config2 = {} as SenseConfiguration;   
this.config2.tipo = &quot;infeed&quot;;   
this.config2.dataadclient = &quot;ca-pub-xxxxx&quot;;   
this.config2.dataadformat = &quot;fluid&quot;;   
this.config2.dataadslot = &quot;99999999&quot;;   
this.config2.dataadqlayoutkey = &quot;xxxxxx&quot;;   
this.config2.style = &quot;display:block&quot;;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;the 1.0.0 version released!&lt;/p&gt;

&lt;p&gt;=)&lt;/p&gt;
</content>
      
        <category term="adsense"/>
      
        <category term="angular"/>
      
        <category term="ads"/>
      
        <category term="ng"/>
      
    </entry>
  
    <entry>
      <title type="html">Master-Detail Validation with Tokens for Store Security</title>
      <link href="http://blog.devcito.org/validacion-maestro-detalle-con-tokens-para-seguridad/" rel="alternate" type="text/html" title="Master-Detail Validation with Tokens for Store Security"/>
      <published>2023-09-15T00:00:00+00:00</published>
      <updated>2023-09-15T00:00:00+00:00</updated>
      <id>http://blog.devcito.org/validacion-maestro-detalle-con-tokens-para-seguridad</id>
      <content type="html" xml:base="http://blog.devcito.org/validacion-maestro-detalle-con-tokens-para-seguridad/">&lt;p&gt;When we, as programmers, need to ensure acceptable security in our applications, we face challenges such as validations where we must prevent users from modifying data that doesn’t belong to them and for which they don’t have permissions.&lt;/p&gt;

&lt;p&gt;We have frameworks, languages, and libraries that allow us, through roles, permissions, tokens, sessions, and others, to secure our developments, at least on the application side. However, there are validations that these tools don’t cover. I’m talking about validating the internal logic of the application when entering data into a table. For instance, let’s describe a case where it’s easy to validate a set of data and another case where it’s more complex and difficult to efficiently validate, especially when there’s more than one detail or even a chain of tables.&lt;/p&gt;

&lt;h2 id=&quot;case-study&quot;&gt;Case Study&lt;/h2&gt;

&lt;p&gt;We have two tables: a “sales” table and a “sales_detail” table. Users can enter, modify, and sell data, but only in the “store” where they work. Therefore, we would assume some fields as follows:&lt;/p&gt;

&lt;h2 id=&quot;sales-table&quot;&gt;Sales Table&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Id (sequential)&lt;/li&gt;
  &lt;li&gt;store_id&lt;/li&gt;
  &lt;li&gt;user&lt;/li&gt;
  &lt;li&gt;description&lt;/li&gt;
  &lt;li&gt;client&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;sales-detail-table&quot;&gt;Sales Detail Table&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Id (sequential)&lt;/li&gt;
  &lt;li&gt;sale_id&lt;/li&gt;
  &lt;li&gt;product_id&lt;/li&gt;
  &lt;li&gt;price&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Where sale_id is a Foreign Key with the Sales table)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/2b0ee2c8d59943285bf77f31dd41c052.png&quot; alt=&quot;2b0ee2c8d59943285bf77f31dd41c052.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;security-requirements&quot;&gt;Security Requirements&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Users should only be able to add sales from the store where they are working.&lt;/li&gt;
  &lt;li&gt;Users should only be able to add details from the store where they are working.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;solutions-for-the-sales-table&quot;&gt;Solutions for the Sales Table&lt;/h2&gt;

&lt;p&gt;Considering that there are user data in some authentication and authorization medium when calling our endpoint, a simple solution is to compare the storeid of the table against the storeid or the allowedid for the session user. If they match or are on this list, allow the sale record to be saved in the table.&lt;/p&gt;

&lt;p&gt;This is common to do; sometimes developers forget, a problem that increases more in a REST architecture where requests don’t depend on anything prior to being executed. Likewise, these types of validations that contrast the table’s storeid against the user’s permissions are simple to do, and we could do it against more ids and more tables for a more complex validation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/98bdf077b9c6e523e4859f44e4662414.png&quot; alt=&quot;98bdf077b9c6e523e4859f44e4662414.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Figure: Validation of the store entered with the user’s store, all the data needed for validation is available.&lt;/p&gt;

&lt;h2 id=&quot;solutions-for-the-sales-detail-table&quot;&gt;Solutions for the Sales Detail Table&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/19c3489976ab515c062857a68a8fcf07.png&quot; alt=&quot;19c3489976ab515c062857a68a8fcf07.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Figure: Any sale is valid, even if it’s not from your store, creating a security hole in the application.&lt;/p&gt;

&lt;p&gt;In this table, validations become a bit more complex since, although we still have or always have user data and their allowed stores, what we don’t have is the store_id of the sales table. Generally, the reality is that the developer is more focused on ensuring that their module or development works correctly and, not seeing this problem, either underestimates or omits it. However, it can be a significant security problem considering the following:&lt;/p&gt;

&lt;p&gt;In the salesdetail table, the user could enter a saleid from another store that doesn’t belong to them. However, if they entered closed sales whose totals were already calculated and processed, it might not be taken into account by a state machine. But if they entered a sale_id from another store that is being processed, they could sell a product without the client even realizing that they are being sold more, or even worse, remove a product, or modify the quantity of products, causing problems with sales, stores, sellers, and many more issues.&lt;/p&gt;

&lt;p&gt;To address this, we have logical solutions to perform consistent validation, some of which we detail below:&lt;/p&gt;

&lt;p&gt;To validate, and since we don’t have the id of the sales table in the detail, what we could do is go against the master table and get the id to, once we have it, validate in the same way as with the master table. However, if we have a larger or more massive number of details, the number of calls to the master table to do this validation could cause system slowness. However, for small systems, it’s a logical validation and not very complex in reality.&lt;/p&gt;

&lt;p&gt;Another alternative would be to return to the fields to validate in this case storeid as part of the fk key that is sent in the request and is automatically validated with the database for its existence and also validated against the session, the same as was done in the master table. This option would be the most feasible, but it would require that the ids to validate in this case the storeid were part of the FK key along with the detail id, ensuring that the validation against the stores with which the user has permission is made before entering the table.&lt;/p&gt;

&lt;p&gt;But what happens if we are forced from the design to comply with rules like normalization that wouldn’t allow us to keep the fields in each table, and what happens if there are more details of the details in a chain, the redundancy of the data would be much greater than two tables, then we have to resort to another type of validations, here we come with my solution proposal, a solution that I consider more in line with large and scalable systems.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/02cca555ba1c8243ce94ac224132593c.png&quot; alt=&quot;02cca555ba1c8243ce94ac224132593c.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Figure: If validated, an error 403 is triggered because that sale doesn’t belong to your assigned store.&lt;/p&gt;

&lt;h2 id=&quot;proposed-solution&quot;&gt;Proposed Solution&lt;/h2&gt;

&lt;p&gt;The proposed solution avoids going to the database to make the query in what could be a large table, although it involves some processing, but without the use of queries or other things, it should cost much less than other solutions, making it a better solution than the others.&lt;/p&gt;

&lt;p&gt;For this, we will use a token that contains the information of the id of the record that this user has access to, that is, the saleid where they have already entered, in some other cases they could have other ids that would vary according to the type of validation we want to perform, for this case this signed token must contain the saleid and the user id together and signed to be able to validate the user who has permission to the sale detail and can allow operations, this use could also have variations of states or operations on the table but for our example, it would only have the general information of the sale as well as the user so that it doesn’t serve more users.&lt;/p&gt;

&lt;p&gt;For the token, we have two options that we could use, so that the client sends with the request and it would depend on the user to see which one to use:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;JWT&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;HMAC&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A JWT would have the advantage of having expiration dates, other extra algorithm data, the possibility of sending more data, and as a disadvantage the verification of the signature that would take a little longer than an HMAC but its versatility would give us advantages of more uses or combine other data to validate for extra logic.&lt;/p&gt;

&lt;p&gt;An HMAC can send any data, and the hash is the signature to verify, these data could be used for validation, however, the token serves forever so the table would have to have some protection mechanism to prevent more data from being entered for that sale and the client forgets the token for greater security.&lt;/p&gt;

&lt;p&gt;In our case study, we can use either of the two, JWT is the most known and our choice in this case that will be generated once the data is created in the master Sales table and saved by the client in a safe place, when the client requires sending data from the detail table.&lt;/p&gt;

&lt;h2 id=&quot;steps-to-follow-to-send-data-to-the-table-with-validation&quot;&gt;Steps to follow to send data to the table with validation:&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Once the client requires performing actions on the “sales_detail” table, they send the token along with the request as a header (in cases like forms they could send it as a hidden field if it’s not an object or a body).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Once on the server, the backend should verify the token signature.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If the signature is valid, the backend will decode the data sent in the token to a verifiable format.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The data is verified and compared with&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;the data sent and with the session data, in this case, verify that the saleid is the same as the saleid sent in the body and the userid compared with the userid of the session or the authorization method used.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;If everything verified is correct, then the requested action can be performed by the used endpoint, in this example case, use the insert into the table.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If any of the validations fail, then the endpoint will issue a security error and that the permissions are wrong.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see, this will prevent users from being able to edit, create, or delete information from other stores, as well as depending on how the validation is also preventing information from already closed sales from being entered, as in the use of JWT automatically since the generated JWTs would already be expired when the sale is closed, logic that the developer will have to see which is the best and which gives him more security.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/df8176ad14c01d6d364e1c743d1c3044.png&quot; alt=&quot;df8176ad14c01d6d364e1c743d1c3044.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Figure: Solution with a token that includes the missing information for validation; it is considered valid since it is signed at the moment it is validated in the master table.&lt;/p&gt;

&lt;h2 id=&quot;library-solution&quot;&gt;Library Solution&lt;/h2&gt;

&lt;p&gt;To solve this problem more simply, a library has been built in Java to help perform these types of validations with the following features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Generates the token with the information to be validated.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Easily choose between the two types of tokens (JWT and HMAC).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Configure the algorithm in the case of using JWT.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Validate the integrity of the token.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Retrieve the data for its validation.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use the library with Maven:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/0facf6b41e6535943f05429d395c79ee.png&quot; alt=&quot;0facf6b41e6535943f05429d395c79ee.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Token Generation&lt;/p&gt;

&lt;p&gt;Here we have the example of how it could be generated and responded in a header to be read by the client:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/5052a163928d06ff705bba019d4d1989.png&quot; alt=&quot;5052a163928d06ff705bba019d4d1989.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the user and the sale to the detail will be sent signed, where as it is signed it will be assumed that there is permission on this sale. Bearing in mind that the storeid should also be validated, it is guaranteed that this saleid can be edited.&lt;/p&gt;

&lt;p&gt;Token Validation&lt;/p&gt;

&lt;p&gt;The example shows how the token would be retrieved from the headers sent by the client and a basic validation:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/884b9e084966abf6d1ed7205f82c06b3.png&quot; alt=&quot;884b9e084966abf6d1ed7205f82c06b3.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The library will save us the time of writing code to generate and validate the token, as well as retrieve the data to validate easily. With all this, we have been able to overcome a major security problem when developing that we often don’t take into account or solve in other ways or directly by having large teams some don’t take it into account and that can cause major problems in the future if a user or a hacker manages to exploit this problem, although with a different design, for example, having large and alphanumeric keys, the most optimal are always sequential, and with these tokens, we can protect any endpoint.&lt;/p&gt;

&lt;p&gt;References&lt;/p&gt;

&lt;p&gt;Libreria en maven&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://central.sonatype.com/artifact/io.github.programmercito/DataSealGuard&quot;&gt;Maven Central: io.github.programmercito:DataSealGuard (sonatype.com)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Código fuente de la librería&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Programmercito/datasealguard-java&quot;&gt;Programmercito/datasealguard-java (github.com)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfil de github&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Programmercito&quot;&gt;Programmercito (Programmercito) (github.com)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repositorio de ejemplos&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Programmercito/datasealguard-java-examples&quot;&gt;Programmercito/datasealguard-java-examples (github.com)&lt;/a&gt;&lt;/p&gt;
</content>
      
        <category term="security"/>
      
        <category term="validation"/>
      
        <category term="tokens"/>
      
        <category term="jwt"/>
      
        <category term="hmac"/>
      
        <category term="erp"/>
      
    </entry>
  
    <entry>
      <title type="html">La Importancia de una Arquitectura Base de Código</title>
      <link href="http://blog.devcito.org/la-importancia-de-una-arquitectura-base-de-codigo/" rel="alternate" type="text/html" title="La Importancia de una Arquitectura Base de Código"/>
      <published>2023-08-21T00:00:00+00:00</published>
      <updated>2023-08-21T00:00:00+00:00</updated>
      <id>http://blog.devcito.org/la-importancia-de-una-arquitectura-base-de-codigo</id>
      <content type="html" xml:base="http://blog.devcito.org/la-importancia-de-una-arquitectura-base-de-codigo/">&lt;p&gt;Desarrollar aplicaciones o sistemas ERP escalables con un alto número de líneas de código es una tarea compleja que requiere una planificación cuidadosa y un enfoque estructurado. Una estrategia comúnmente recomendada en este contexto es la creación de una arquitectura base de código sólida. Esta arquitectura base se convierte en el cimiento sobre el cual se construirá todo el sistema, y está compuesta por bibliotecas, módulos y código base que abordan tareas comunes y repetitivas, como la creación de pantallas, APIs y otras funcionalidades esenciales de la aplicación.&lt;/p&gt;

&lt;p&gt;La razón principal detrás de desarrollar una arquitectura base de código es la coherencia y la eficiencia. En proyectos de gran envergadura, es fácil que los desarrolladores utilicen diferentes frameworks, bibliotecas y enfoques, lo que puede llevar a una falta de uniformidad en la estructura del código y dificultar el mantenimiento a largo plazo. Al proporcionar una arquitectura base, se establecen directrices claras para el desarrollo, lo que asegura que todos los miembros del equipo sigan un conjunto común de estándares y prácticas. Esto facilita la colaboración y permite que el código sea más legible y mantenible en el tiempo.&lt;/p&gt;

&lt;p&gt;Además de fomentar la coherencia, una arquitectura base de código también puede mejorar la productividad. Los desarrolladores no necesitarán reinventar la rueda cada vez que enfrenten una tarea común, ya que podrán aprovechar los componentes y módulos predefinidos. Esto acelera el proceso de desarrollo y reduce la probabilidad de errores.&lt;/p&gt;

&lt;p&gt;Para asegurar el éxito de una arquitectura base de código, es esencial llevar a cabo un análisis profundo de los requisitos del sistema y de las necesidades de la aplicación. Esto permitirá identificar las características y funcionalidades comunes que deben ser parte de la arquitectura base. Algunos elementos clave pueden incluir:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Librerías y Frameworks Unificados: Seleccionar un conjunto coherente de librerías y frameworks que sean adecuados para el proyecto y que todos los desarrolladores utilicen. Esto evita la proliferación de tecnologías dispersas en el sistema.&lt;/li&gt;
  &lt;li&gt;Patrones de Diseño: Definir patrones de diseño estándar que guíen la estructura de la aplicación. Esto asegura que la arquitectura sea escalable y adaptable a medida que el sistema crece.&lt;/li&gt;
  &lt;li&gt;Módulos Reutilizables: Desarrollar módulos y componentes reutilizables que aborden funciones comunes. Esto incluye plantillas para pantallas, lógica de negocios genérica y componentes de interfaz de usuario.&lt;/li&gt;
  &lt;li&gt;Documentación Detallada: Proporcionar documentación exhaustiva sobre la arquitectura base, explicando cómo se deben utilizar los componentes, las pautas de codificación y las mejores prácticas.&lt;/li&gt;
  &lt;li&gt;Actualización Continua: Mantener la arquitectura base actualizada con las últimas tecnologías y mejores prácticas. Esto garantiza que el sistema siga siendo eficiente y relevante a lo largo del tiempo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En resumen, desarrollar una arquitectura base de código para aplicaciones o sistemas ERP de gran tamaño es una estrategia efectiva para promover la coherencia, la eficiencia y la productividad en el desarrollo. Al proporcionar un conjunto sólido de estándares y componentes, se establece una base sólida para el crecimiento y el mantenimiento a largo plazo del sistema.&lt;/p&gt;
</content>
      
        <category term="arquitectura"/>
      
        <category term="codigo"/>
      
        <category term="erp"/>
      
        <category term="desarrollo"/>
      
    </entry>
  
    <entry>
      <title type="html">Usando JWT para Mostrar información sin Base de Datos, tampoco backend</title>
      <link href="http://blog.devcito.org/info-with-jwt-with-out-db-and-backend/" rel="alternate" type="text/html" title="Usando JWT para Mostrar información sin Base de Datos, tampoco backend"/>
      <published>2022-09-25T00:00:00+00:00</published>
      <updated>2022-09-25T00:00:00+00:00</updated>
      <id>http://blog.devcito.org/info-with-jwt-with-out-db-and-backend</id>
      <content type="html" xml:base="http://blog.devcito.org/info-with-jwt-with-out-db-and-backend/">&lt;p&gt;En el mundo moderno del desarrollo web, a menudo buscamos soluciones que reduzcan la complejidad y los costos. Una idea innovadora es usar JSON Web Tokens (JWT) para transportar y validar datos sin la necesidad de una base de datos. En este blog, exploraremos cómo puedes mostrar información usando JWT en un enlace y un sitio web, sin necesidad de usar un backend, usaremos Java para la generación del token sin levantar ninguna backend ( podríamos usar cualquier otro lenguaje inclusive typescript sin levantar ningún servicio publicado) y Angular para el frontend.&lt;/p&gt;

&lt;p&gt;Firma Asimétrica y JWT&lt;/p&gt;

&lt;p&gt;La firma asimétrica utiliza un par de claves: una clave privada para firmar el JWT y una clave pública para verificar la firma. Esto significa que mientras el emisor firma el JWT con su clave privada, cualquier parte que tenga la clave pública correspondiente puede verificar la autenticidad del JWT, pero no puede modificarlo ni volver a firmarlo.&lt;/p&gt;

&lt;p&gt;¿Cómo se utiliza JWT con firma asimétrica para transmitir información?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Creación del JWT:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;El emisor define el payload con la información que desea compartir.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;El JWT se firma usando un algoritmo de firma asimétrica y la clave privada del emisor.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;El JWT firmado se envía al receptor.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2. Recepción y Validación del JWT:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;El receptor obtiene el JWT.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Se valida la firma del JWT usando la clave pública del emisor.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Si la validación es exitosa, el payload se decodifica y se utiliza la información.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;img src=&quot;../assets/images/fce9fbd5bc35bd0a0bbaf4d6d42413f2.png&quot; alt=&quot;fce9fbd5bc35bd0a0bbaf4d6d42413f2.png&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué JWT?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JWT es un estándar abierto que define una forma compacta y autónoma de transmitir información entre partes en forma de objeto JSON. Esta información puede ser verificada y confiada porque está firmada digitalmente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ventajas&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Simplicidad&lt;/strong&gt;: Elimina la necesidad de bases de datos o backends complejos.&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Integridad de datos&lt;/strong&gt;: La firma garantiza que los datos no han sido alterados.&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Distribución&lt;/strong&gt;: Un simple enlace con el JWT permite acceder al valor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Seguridad: &lt;/strong&gt;Al ser un token firmado el contenido de este no se puede modificar y si se lo hace el sitio web no lo mostrara, haciendo que lo que se mande no se pueda cambiar para mostrar otra cosa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desafíos y consideraciones&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Generación del JWT&lt;/strong&gt;: Aunque no necesitas un backend para servir los datos, aún necesitarías algún mecanismo o proceso para generar el JWT con el valor de la moneda.&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Expiración del JWT&lt;/strong&gt;: Los JWT generalmente tienen un tiempo de expiración. Si alguien intenta acceder al enlace después de la expiración, no podrá ver el valor.&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Tamaño y eficiencia&lt;/strong&gt;: Si bien un JWT es relativamente pequeño, es posible que no sea el método más eficiente para transportar un simple valor numérico.&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Seguridad&lt;/strong&gt;: Aunque el JWT garantiza la integridad de los datos, no garantiza su confidencialidad. Cualquiera puede decodificar un JWT y ver su contenido.&lt;/p&gt;

&lt;p&gt;- &lt;strong&gt;Dependencia de la clave&lt;/strong&gt;: Si la clave privada utilizada para firmar el JWT se pierde o se ve comprometida, tendrías que generar una nueva y actualizar cualquier lógica del lado del cliente que use la clave pública para la verificación.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementación&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Generación del JWT con Java&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/cbc6955b3cfa68cdd5d47ca56323211d.png&quot; alt=&quot;cbc6955b3cfa68cdd5d47ca56323211d.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Validación y visualización con Angular&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Para el frontend, usaremos Angular con TypeScript. Hay varias bibliotecas disponibles para manejar JWT en TypeScript, como jsrsasign.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/b5aebbf32331649fc7a2a3c8818e1849.png&quot; alt=&quot;b5aebbf32331649fc7a2a3c8818e1849.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Funcionamiento&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Hasta este punto sabemos bien como una firma asimétrica es usa en jwt para enviar información que en el camino no se puede modificar, pero como se hace.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Usaremos la libreria JwtLinkDinamic&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;
 &amp;lt;groupId&amp;gt;io.github.programmercito&amp;lt;/groupId&amp;gt;&lt;br /&gt;
 &amp;lt;artifactId&amp;gt;JwtLinkDinamic&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
 &amp;lt;version&amp;gt;1.0.1&amp;lt;/version&amp;gt;&lt;br /&gt;
&amp;lt;/dependency&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;2. Aca ejemplo de uso, puede usarse en un backend, aun que la idea es que no se use en uno , si no en una pc local que no encesita estar prendida todo el tiempo:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;public static void main(String[] args) {&lt;br /&gt;
 String link = “&lt;a href=&quot;https://programmercito.github.io/jwtlink-web-example/#/example/&quot;&gt;https://programmercito.github.io/jwtlink-web-example/#/example/&lt;/a&gt;${jwt}”;&lt;br /&gt;
 MonedaPrecio monedap = new MonedaPrecio();&lt;br /&gt;
 monedap.setMoneda(“Dolar”);&lt;br /&gt;
 monedap.setPrecio(33.23);&lt;br /&gt;
 String resul = JwtMessage.generate(link, monedap);&lt;br /&gt;
 System.out.println(resul);&lt;br /&gt;
}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Aca vemos el resultado:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://programmercito.github.io/jwtlink-web-example/#/example/eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE3NTg3NTQwNDUsInYiOiJ7XCJtb25lZGFcIjpcIkRvbGFyXCIsXCJwcmVjaW9cIjozMy4yM30ifQ.bFApTsTqufmxrc7p6Eu9KYvVGsrlmJPV2VMbIdT6I8FMSJzUnA0ERqGBLKPspfN3uzB0rkw-Ftu97EkVqcoQIgwsMKw2wa_7EJW8TTp9imQ5nXv2H-Z5Jk43O6JKYZrb7b87pQkf-uajVjWRmWDmsN5l457MiG7Ln0_H7v0uAQsI251UWyLCTvsyAUkm1c2sRBPBVHoTDERSNoWFdWfZLDyoviyNB_iPUHVcTPxMJZAXd6Dg0MNC-TdyAwPSXp94J_lXjzn7x_o4-IExY19JlrJBoZ5lRZAQIi57y92U0lZNlTrjPZI7jkH_CnKpGv3qaVTqq7V35cCG2BjE8wH79A&quot;&gt;https://programmercito.github.io/jwtlink-web-example/#/example/eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE3NTg3NTQwNDUsInYiOiJ7XCJtb25lZGFcIjpcIkRvbGFyXCIsXCJwcmVjaW9cIjozMy4yM30ifQ.bFApTsTqufmxrc7p6Eu9KYvVGsrlmJPV2VMbIdT6I8FMSJzUnA0ERqGBLKPspfN3uzB0rkw-Ftu97EkVqcoQIgwsMKw2wa_7EJW8TTp9imQ5nXv2H-Z5Jk43O6JKYZrb7b87pQkf-uajVjWRmWDmsN5l457MiG7Ln0_H7v0uAQsI251UWyLCTvsyAUkm1c2sRBPBVHoTDERSNoWFdWfZLDyoviyNB_iPUHVcTPxMJZAXd6Dg0MNC-TdyAwPSXp94J_lXjzn7x_o4-IExY19JlrJBoZ5lRZAQIi57y92U0lZNlTrjPZI7jkH_CnKpGv3qaVTqq7V35cCG2BjE8wH79A&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3. Para poder desplegar la apllicacion en angular como dijimos no se usara un backend pero si un frontend, un ejemplo se ha desplegado en:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Programmercito/jwtlink-web-example&quot;&gt;Programmercito/jwtlink-web-example (github.com)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;aca el codigo de llamado al JWT:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;import { Component, OnInit } from ‘&lt;a href=&quot;https://twitter.com/angular/core&quot;&gt;@angular/core&lt;/a&gt;’;&lt;br /&gt;
import { ActivatedRoute } from ‘&lt;a href=&quot;https://twitter.com/angular/router&quot;&gt;@angular/router&lt;/a&gt;’;&lt;br /&gt;
import { ValidateService } from ‘src/app/core/validate.service’;&lt;br /&gt;
import { Buffer } from ‘buffer’;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/Component&quot;&gt;@Component&lt;/a&gt;({&lt;br /&gt;
 selector: ‘example’,&lt;br /&gt;
 templateUrl: ‘./example.component.html’,&lt;br /&gt;
 styleUrls: [‘./example.component.sass’]&lt;br /&gt;
})&lt;br /&gt;
export class ExampleComponent implements OnInit {&lt;br /&gt;
 dato: string = “”;&lt;br /&gt;
 valido: boolean = true;&lt;br /&gt;
 objeto: any;&lt;br /&gt;
 constructor(private active: ActivatedRoute, private valid: ValidateService) {&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;}&lt;br /&gt;
 ngOnInit(): void {&lt;br /&gt;
 this.dato = this.active.snapshot.paramMap.get(“data”) ?? “”;&lt;br /&gt;
 if (this.valid.valid(this.dato)) {&lt;br /&gt;
 let jwtpartido: string[] = this.dato.split(“.”);&lt;br /&gt;
 let base: string = Buffer.from(jwtpartido[1], ‘base64’).toString(“utf8”);&lt;br /&gt;
 let json: any = JSON.parse(base);&lt;br /&gt;
 this.objeto = JSON.parse(json.v);&lt;br /&gt;
 } else {&lt;br /&gt;
 this.valido = false;&lt;br /&gt;
 }&lt;br /&gt;
 }&lt;br /&gt;
}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/763d2db4d9f154a30738e9090dcc4fbe.png&quot; alt=&quot;763d2db4d9f154a30738e9090dcc4fbe.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Una vez accediendo al link podemos ver como hemos accedido a los datos del jwt de manera segura:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/061c9daed7c8135bcc28ac5f2f09ec8c.png&quot; alt=&quot;061c9daed7c8135bcc28ac5f2f09ec8c.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Repo de la aplicación de ejmplo de uso de JWT:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Programmercito/jwtlink-web-example&quot;&gt;Programmercito/jwtlink-web-example (github.com)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Usar JWT para transportar y validar datos es una solución ingeniosa que reduce la complejidad y los costos asociados con bases de datos y backends. Sin embargo, es esencial ser consciente de las limitaciones y desafíos que presenta esta aproximación. Es importante recordar que, aunque puedes decodificar y validar un JWT en el lado del cliente, sin embargo, hay que tomar en cuenta que si o si, debe usarse una firma asimétrica, que los links y su información debería ser publica ya que seguridad en el frontend es siempre relativa, luego esta técnica resulta ser útil a la hora de faltar backend o no se puede gastar en él.&lt;/p&gt;
</content>
      
        <category term="jwt"/>
      
        <category term="db"/>
      
        <category term="info"/>
      
        <category term="data"/>
      
        <category term="backend"/>
      
    </entry>
  
</feed>
