# Pendiente Lo que sabemos que falta o falla, con el diagnóstico ya hecho para no volver a investigarlo desde cero. Ordenado por lo que más duele. --- ## 1. Las capas no son de verdad independientes **Lo que pasa.** Se pueden poner dos capas con visuales MilkDrop, pero acaban siendo **la misma imagen**. Y no hay forma de decir "en esta zona el Mezclador, en esta otra Butter", configurando cada una por su cuenta. Son **tres cosas distintas** mezcladas en el mismo síntoma: ### 1a. Dos capas Butter sin preset elegido salen idénticas — ✅ HECHO `web/stage/layers.js`, en `crearButter()`, hacía: ```js let i = Math.max(0, nombres.indexOf(f.preset)); ``` Con `preset: ""` (lo que quedaba si la lista de presets aún no había llegado al panel al crear la capa), `indexOf("")` devuelve `-1` y el `Math.max(0, -1)` lo dejaba en **0**: todas las capas así arrancaban en el mismo preset. **Cómo quedó:** sin preset elegido, cada instancia coge uno **al azar** y lo devuelve en `presetElegido`. `sync()` avisa por `deps.onPreset(id, preset)`, que `stage.js` (`anotarPresetDeCapa`) escribe en el estado con `set_mapping`, así que el panel enseña cuál le tocó en vez de dejar el desplegable en blanco. No recrea la instancia: la firma de una capa butter sigue siendo el tipo a secas. ### 1b. Las capas puestas en "El motor de MOTORES" comparten imagen — ✅ HECHO Esa fuente es, literalmente, el lienzo del motor activo. Dos capas así van a enseñar lo mismo siempre — no es un fallo, es lo que significa. El problema era que **era la fuente por defecto** de toda capa nueva, así que el primer resultado que veía cualquiera era "dos capas iguales". **Cómo quedó:** una capa nueva ya no nace en "motor". `panel.js` tiene `fuenteDeCapaNueva()`, que la estrena en MilkDrop con preset al azar (contenido distinto desde el primer clic) y solo cae en "motor" si Butterchurn todavía no ha dado su lista de presets. Lo usan tanto `+ Capa` como `Máscara → Capa`. ### 1c. Falta el Mezclador (y Hydra) como fuente de capa — *(el trabajo de verdad)* Hoy las fuentes son: motor, butter, shader, clip, cámara, negro. **No está el Mezclador**, así que "una zona con el Mezclador y otra con Butter" solo se puede hacer vía "motor", y entonces manda el selector global. Por qué no está: el Mezclador **es** Hydra (genera código Hydra y lo ejecuta), y Hydra está montado como instancia única global (`makeGlobal: true`, con `s0`, `s1`, `o0` en el espacio global). Para tener dos mezcladores independientes hay que instanciar Hydra varias veces con `makeGlobal: false` y usar la API por instancia (`h.synth.src(...)`), lo que obliga a reescribir `buildMixerCode` para que no dependa de los nombres globales. **Coste realista:** medio día, más el gasto de GPU de un Hydra por capa. Antes de meterse, decidir si compensa frente a la alternativa barata: dejar el Mezclador solo como motor a pantalla completa y que las capas tiren de MilkDrop/shader/clip, que es lo que ya funciona. ### 1d. Los dos paneles no están relacionados MOTORES y MAPPING van cada uno por su lado: el preset que eliges en el motor Butter y el de una capa MilkDrop son listas distintas, no comparten favoritos, y lo que tocas en uno no se refleja en el otro. **Hacia dónde:** una sola biblioteca de presets y una sola lista de favoritos, usada por el motor Butter, por las capas del mapping y por projectM. Esto se solapa con el punto 2: es el mismo trabajo de unificación. --- ## 2. projectM en el mapper — ✅ HECHO (camino A) **Lo que pasaba.** Al elegir projectM no había forma de mapearlo ni de usarlo en una capa, y ahí está **la biblioteca de verdad**: 9795 presets `.milk` en 11 categorías y 189 visuales. Butterchurn solo trae ~100 propios. **Por qué no se podía.** projectM es un proceso nativo que pinta en su propia ventana X11/Wayland. El compositor de mapping es WebGL dentro del navegador y solo puede usar como textura lo que vive en la página. **Cómo quedó.** Se descartó capturar la ventana (frágil en Wayland) y compilar projectM a WebAssembly (un proyecto en sí). Se hizo el **camino A**: traducir los `.milk` a presets de Butterchurn **en el propio navegador**, al elegirlos. - `web/lib/milkdrop-preset-converter.min.js` (npm `milkdrop-preset-converter`, añadido a `web/package.json` y a `install.sh`). - `layers.js` → `cargarMilk(ruta)`: pide el `.milk` al backend (ya los servía), lo convierte y se lo pasa a `viz.loadPreset()`. Cachea **la promesa**, no el resultado, para que dos capas pidiendo el mismo preset lo conviertan una vez. - La capa MilkDrop tiene ahora `biblioteca: "butter" | "projectm"`. La firma de la capa **sigue siendo `'butter'`**, así que cambiar de biblioteca no recrea la instancia ni gasta un contexto WebGL más. - El panel tiene selector de biblioteca y, con projectM, los tres niveles (categoría / visual / variante) más un botón de "uno al azar". **Medido, no supuesto:** - **100/100** presets de una muestra repartida por toda la biblioteca se convierten sin excepción, incluidos **84/84** de los que llevan shaders `warp_1`/`comp_1` (que eran justo los dudosos). - Conversión de un `.milk` real en el navegador: **7 ms**. - Una capa del mapper con `Dancer/Glowsticks/285.milk` renderiza sin un solo aviso del escenario. > Cuidado al medir esto: si el `.milk` no se descarga, Butterchurn **sigue > pintando su preset de reserva**. Una prueba que solo mire "¿hay píxeles > encendidos?" da OK igual. Por eso la comprobación exige además que el > escenario no haya soltado ningún aviso. La primera versión de la prueba dio > un falso OK exactamente por esto. **Lo que queda de este punto:** la conversión no es perfecta al 100 % en fidelidad — algún shader complejo puede verse distinto del original en projectM. Se convierte y se ve, pero si un preset no convence, la salida es probar otra variante. Y falta unificar los favoritos entre el motor projectM y las capas (parte del punto 1d). --- ## 3. Los clips salían del revés en el mapping — ✅ ARREGLADO **Lo que pasaba.** Al poner un vídeo en una capa del mapping, salía volteado verticalmente (boca abajo). **Por qué.** `web/stage/mapper.js`, en `initGL()`, hacía `gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)`. Pero el modelo del mapper va con el origen **arriba-izquierda** (`vUV.y = 0` es el borde de arriba de la superficie) y `texImage2D` ya sube la primera fila de la imagen en `t = 0`. Con el flip puesto, `t = 0` pasaba a ser la ÚLTIMA fila: todo del revés. Se notaba con los clips porque una visual de MilkDrop es casi simétrica y no canta. **Comprobado**, no deducido: con una fuente mitad roja arriba / mitad azul abajo, renderizando el `mapper.js` real en Chromium headless y leyendo el píxel de arriba con `readPixels`, antes salía AZUL y ahora sale ROJO. De paso se midió el **Mezclador** (Hydra, `s1.init({src})`) con la misma prueba: ese sale bien, no hay que tocarlo. Si alguna vez se ve del revés ahí, no es este fallo. > Al medir esto, ojo con `readPixels` sobre un lienzo sin > `preserveDrawingBuffer`: devuelve negro y es facilísimo leerlo como "está > volteado". Hay que reservar el contexto con `preserveDrawingBuffer: true` > ANTES de que la librería llame a `getContext`. --- ## 4. Menor: no hay fundido al cambiar de motor Pasar de Mezcla a Butter es un corte seco. Dentro de una capa MilkDrop sí hay transición suave (deslizador de 0–8 s), pero cruzar **dos motores** exige renderizar los dos a la vez durante el cruce y fundirlos en el compositor. Se puede hacer en el mapper (dibujar la capa dos veces, con la fuente vieja bajando de alfa y la nueva subiendo), pero cuesta tener los dos motores vivos a la vez unos segundos. Decidir si merece la pena.