Mapping in-browser (Fase 1) + trabajo en curso

Projection mapping editable desde el navegador, sin herramientas externas:
- web/stage/mapper.js (nuevo): compositor WebGL que texturiza el motor activo
  sobre superficies deformables (homografia por vertice: keystone correcto).
- Editor con raton en el escenario (crear, arrastrar esquinas, mover, borrar)
  y previsualizacion arrastrable en el panel (raton/tactil) para editar desde
  el movil sin tocar la ventana de visuales.
- Backend: estado "mapping" + evento set_mapping, persistido en data/mapping.json
  (ignorado en git). Seccion "Mapping" en el panel + texto de ayuda.
- Solo motores web (butterchurn/hydra/shaders/mixer); projectM no se mapea.
- .gitignore: excluye config de runtime (mapping.json, pm-favoritos.json).

Incluye ademas trabajo en curso previo sin commitear (server.py, stage.js,
panel.js/css, ayuda.json, hydra-sketches.json, problemas.md,
start-projectm.sh), subido junto a peticion del autor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hacklab 2026-07-27 00:19:30 +02:00
parent 0b1546cecc
commit 8dd58bb293
12 changed files with 1815 additions and 85 deletions

View file

@ -40,15 +40,21 @@
<span class="label">Audio</span>
<button class="info" data-help="audio">i</button>
</div>
<div class="seg" id="audio-source">
<button id="audio-src-mic" class="segbtn" data-src="mic">Microfono (sala)</button>
<button id="audio-src-monitor" class="segbtn" data-src="monitor">Audio del navegador</button>
</div>
<select id="audio-device"><option>Detectando entradas...</option></select>
<button id="audio-apply" class="cmd accent">Aplicar microfono</button>
<button id="audio-apply" class="cmd accent">Aplicar entrada</button>
<button id="dev-rescan" class="cmd">Buscar dispositivos de nuevo</button>
<div class="row">
<span class="label">BPM detectado</span>
<span id="bpm" class="bpm">--</span>
</div>
<p class="hint">Si conectas el microfono o la camara con el equipo ya
encendido, pulsa este boton para que aparezcan.</p>
<p class="hint">Con <b>Audio del navegador</b> las visuales reaccionan a
lo que suena en el equipo (Spotify, YouTube...) aunque lo oigas por los
cascos. Si conectas un microfono con el equipo ya encendido, pulsa
Buscar dispositivos de nuevo.</p>
</section>
<!-- Sensibilidad -->
@ -61,9 +67,9 @@
<input id="sens" type="range" min="0" max="4" step="0.1" value="1">
</section>
<!-- Estado -->
<!-- Estado: visual que se esta proyectando ahora mismo -->
<section class="card" id="card-now">
<span class="label">Ahora suena</span>
<span class="label">En pantalla</span>
<div id="now" class="now">-</div>
</section>
@ -153,15 +159,29 @@
<button class="segbtn" data-src="video">Video</button>
<button class="segbtn" data-src="mix">Mezcla</button>
</div>
<div class="row">
<span class="label">Fondo (capa de abajo)</span>
<select id="mix-fondo" class="inline">
<option value="cam">Camara</option>
<option value="butter">Visuales Butter (MilkDrop)</option>
</select>
</div>
<label class="check">
<input type="checkbox" id="mix-cam"> Camara activada
</label>
<select id="mix-camera"><option value="0">Camara por defecto</option></select>
<select id="mix-video"><option value="">-- sin video --</option></select>
<button id="mix-rescan" class="cmd">Actualizar lista de videos</button>
<div class="row">
<button id="mix-rescan" class="cmd">Actualizar lista</button>
<button id="mix-upload-btn" class="cmd">Subir video o imagen</button>
<span id="mix-upload-status" class="hint"></span>
</div>
<input type="file" id="mix-upload" hidden
accept="video/*,image/jpeg,image/png,image/gif,image/webp">
<div class="row">
<span class="label">Modo de mezcla</span>
<select id="mix-blend" class="inline">
<option value="recorte">Recorte (personaje delante)</option>
<option value="blend">Fundido</option>
<option value="diff">Diferencia</option>
<option value="mult">Multiplicar</option>
@ -184,10 +204,75 @@
<span class="label">projectM</span>
<button class="info" data-help="projectm">i</button>
</div>
<select id="pm-category">
<option value="">Todas las categorias</option>
</select>
<select id="pm-preset">
<option value="">-- rotando (elige uno para fijarlo) --</option>
</select>
<select id="pm-variante">
<option value="">-- variantes: rotando (elige una a dedo) --</option>
</select>
<div class="row">
<button class="cmd" data-action="prev">&laquo; Anterior</button>
<button class="cmd" data-action="next">Siguiente &raquo;</button>
</div>
<div class="row">
<button id="pm-lock" class="cmd">Congelar visual</button>
<button id="pm-fix" class="cmd">Fijar este estilo</button>
</div>
<p class="hint" id="pm-now">En pantalla: ...</p>
<div class="cardhead">
<span class="label">Favoritos (para pinchar en directo)</span>
</div>
<div class="row">
<button id="pm-fav-add" class="cmd accent">&#9733; Guardar el de pantalla</button>
</div>
<div id="pm-fav-list"></div>
<label class="check">
<input type="checkbox" id="pm-shuffle"> Orden aleatorio
</label>
<div class="row">
<span class="label">Pantalla</span>
<select id="pm-monitor" class="inline">
<option value="auto">Auto (la del escenario)</option>
</select>
</div>
<div class="row">
<span class="label">Segundos por visual: <span id="pm-dur-val">60</span> s</span>
</div>
<input id="pm-duration" type="range" min="5" max="600" step="5" value="60">
<p class="hint">Congelar deja clavado el visual que se ve (al momento,
sin cortes). Fijar este estilo lo apunta en las listas y projectM
rota solo entre variantes de ese look. Cada cambio de listas o
deslizador relanza projectM (~1 s en negro).</p>
</section>
<!-- Mapping (projection mapping) -->
<section class="card" id="card-mapping">
<div class="cardhead">
<span class="label">Mapping</span>
<button class="info" data-help="mapping">i</button>
</div>
<label class="check">
<input type="checkbox" id="map-enabled"> Activar mapping
</label>
<label class="check">
<input type="checkbox" id="map-edit"> Modo edicion (arrastrar en el escenario)
</label>
<div class="row">
<button id="map-add" class="cmd accent">+ Anadir superficie</button>
<button id="map-reset" class="cmd">Reset</button>
</div>
<span class="label">Previsualizacion (arrastra las esquinas)</span>
<canvas id="map-preview"
style="width:100%;aspect-ratio:16/9;background:#0a0a0a;border:1px solid #333;border-radius:6px;display:block;margin:6px 0;touch-action:none;cursor:crosshair;"></canvas>
<div id="map-list"></div>
<p class="hint">1) Elige un motor que no sea projectM. 2) Pulsa
<b>Anadir superficie</b>. 3) <b>Arrastra las esquinas</b> aqui en la
previsualizacion (raton o dedo) para encajar la forma; se ve en el
proyector al instante. Cada superficie mapea la misma imagen. Se
guarda solo.</p>
</section>
</div>