Panel: fuente Xirod, titulos centrados y diseno de dos columnas

Anade la fuente Xirod (web/panel/fonts/xirod.otf, de 1001fonts) para el titulo del panel, tanto en Raspberry como en portatil. Titulos centrados en la cabecera y en cada tarjeta. En pantalla ancha el panel pasa a dos columnas para aprovechar el espacio del portatil (Visuales/Audio/Sensibilidad a la izquierda, Motor y controles a la derecha); en movil sigue en una sola columna, igual que antes. El README explica el uso en ordenador con diagrama, comandos y tabla de diferencias, recalcando que en ordenador no se usa el modo kiosko.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hacklab 2026-05-22 16:28:17 +02:00
parent c8b51615ba
commit 7ebb593288
5 changed files with 281 additions and 182 deletions

View file

@ -2,11 +2,15 @@
# FOSFENO
**Motor de visuales audio-reactivas para Raspberry Pi.**
**Motor de visuales audio-reactivas para Raspberry Pi y para portátil Linux.**
Convierte una Raspberry Pi + un proyector + un micro USB en una estación de
VJ automática: la Pi escucha la música de la sala, detecta su BPM y proyecta
VJ automática: escucha la música de la sala, detecta su BPM y proyecta
visuales que reaccionan al sonido. Todo se controla desde un panel web.
Funciona en dos escenarios: en una **Raspberry Pi** como aparato dedicado que
arranca solo, o en un **portátil Linux** que lanzas cuando quieras. El apartado
[En un portátil Linux](#en-un-portátil-linux) explica las diferencias.
```
[ Música en la sala ]
│ (micro USB)
@ -76,13 +80,7 @@ bash install.sh --no-projectm # omite la compilación de projectM
bash install.sh --check # solo comprueba el sistema, no instala nada
```
### En un portátil Linux
FOSFENO también corre en un portátil con Debian, Ubuntu o Mint, sin Raspberry
Pi. Se instala con `bash install.sh --laptop` y se arranca cuando quieras con
`./fosfeno`. Los detalles están en [FOSFENO en un portátil](docs/portatil.md).
Después:
Después, en la Raspberry, activa el arranque al escritorio:
```bash
sudo raspi-config # System Options → Boot/Auto Login → Desktop Autologin
@ -91,6 +89,49 @@ sudo reboot
Al reiniciar, la Pi arranca sola en modo kiosko mostrando las visuales.
### En un portátil Linux
FOSFENO no necesita la Raspberry: corre igual en un portátil con Debian,
Ubuntu o Mint. El portátil ya trae micrófono y cámara, y lo conectas al
proyector por HDMI como cualquier otra cosa.
```
[ Música de la sala ]
│ (micrófono integrado, o USB)
┌────────────────────────┐
│ Portátil Linux │── HDMI ──▶ [ Proyector :: visuales ]
│ ./fosfeno │
└───────────┬────────────┘
Panel: http://localhost:8080/
(o desde el móvil, en la misma red Wi-Fi)
```
Se instala una vez y se arranca a mano cuando lo necesites:
```bash
bash install.sh --laptop # instala, sin tocar el arranque del sistema
./fosfeno # arranca FOSFENO; Ctrl+C para cerrarlo
```
Diferencias con la Raspberry Pi:
| | Raspberry Pi | Portátil Linux |
|---|---|---|
| Arranque | Automático al encender | A mano, con `./fosfeno` |
| Modo kiosko | Sí: visuales a pantalla completa al arrancar | **No**: ventana normal que mueves al proyector |
| Puerto del panel | 80 — `http://fosfeno.local/` | 8080 — `http://localhost:8080/` |
| Micrófono y cámara | Por USB | Los integrados del portátil |
| Cambios en el sistema | Arranque automático y nombre de red | Ninguno |
La diferencia clave: en el portátil **no se usa el modo kiosko**. Las visuales
salen en una ventana de navegador normal que arrastras a la pantalla del
proyector y pones a pantalla completa con F11. Así FOSFENO no se apodera de tu
pantalla ni se mete en el arranque del sistema; lo abres y lo cierras tú.
Guía completa: [FOSFENO en un portátil](docs/portatil.md).
## Uso
- **Visuales** → salen automáticamente por el proyector (HDMI).

View file

@ -0,0 +1,9 @@
Fuente del panel
================
xirod.otf - fuente "Xirod", usada para el titulo del panel.
Descargada de 1001fonts.com. Comprueba sus condiciones de uso si vas a
distribuir el proyecto con fines comerciales.
Para cambiar la fuente del titulo, sustituye este archivo y ajusta la
regla @font-face de web/panel/panel.css.

BIN
web/panel/fonts/xirod.otf Normal file

Binary file not shown.

View file

@ -20,9 +20,13 @@
<button id="notif-close" aria-label="Cerrar aviso">&times;</button>
</div>
<!-- En movil las tarjetas van en una sola columna; en pantalla ancha,
en dos columnas (.column pasa a display:contents en movil). -->
<main>
<div class="column">
<!-- Encendido -->
<section class="card center">
<section class="card center" id="card-power">
<div class="cardhead">
<span class="label">Visuales</span>
<button class="info" data-help="power">i</button>
@ -30,8 +34,51 @@
<button id="power" class="power-btn">ON</button>
</section>
<!-- Audio: tarjeta + BPM -->
<section class="card" id="card-audio">
<div class="cardhead">
<span class="label">Audio</span>
<button class="info" data-help="audio">i</button>
</div>
<select id="audio-device"><option>Detectando entradas...</option></select>
<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>
</section>
<!-- Sensibilidad -->
<section class="card" id="card-sens">
<div class="cardhead">
<span class="label">Sensibilidad al audio:
<span id="sens-val" class="value">1.0</span></span>
<button class="info" data-help="sensibilidad">i</button>
</div>
<input id="sens" type="range" min="0" max="4" step="0.1" value="1">
</section>
<!-- Estado -->
<section class="card" id="card-now">
<span class="label">Ahora suena</span>
<div id="now" class="now">-</div>
</section>
<!-- Sistema -->
<section class="card sys" id="card-sys">
<button id="reboot" class="sysbtn">Reiniciar</button>
<button id="shutdown" class="sysbtn danger">Apagar</button>
</section>
<p id="net-foot" class="netfoot">FOSFENO</p>
</div>
<div class="column">
<!-- Selector de motor -->
<section class="card">
<section class="card" id="card-engines">
<div class="cardhead">
<span class="label">Motor de visuales</span>
<button class="info" data-help="engines">i</button>
@ -50,32 +97,6 @@
</div>
</section>
<!-- Audio: tarjeta + BPM -->
<section class="card">
<div class="cardhead">
<span class="label">Audio</span>
<button class="info" data-help="audio">i</button>
</div>
<select id="audio-device"><option>Detectando entradas...</option></select>
<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 la Raspberry ya
encendida, pulsa este boton para que aparezcan.</p>
</section>
<!-- Sensibilidad -->
<section class="card">
<div class="cardhead">
<span class="label">Sensibilidad al audio:
<span id="sens-val" class="value">1.0</span></span>
<button class="info" data-help="sensibilidad">i</button>
</div>
<input id="sens" type="range" min="0" max="4" step="0.1" value="1">
</section>
<!-- Butterchurn -->
<section class="card" id="ctl-butterchurn" hidden>
<div class="cardhead">
@ -168,19 +189,7 @@
</div>
</section>
<!-- Estado -->
<section class="card">
<span class="label">Ahora suena</span>
<div id="now" class="now">-</div>
</section>
<!-- Sistema -->
<section class="card sys">
<button id="reboot" class="sysbtn">Reiniciar Pi</button>
<button id="shutdown" class="sysbtn danger">Apagar Pi</button>
</section>
<p id="net-foot" class="netfoot">FOSFENO</p>
</div>
</main>
<!-- Ventana de informacion -->

View file

@ -1,5 +1,13 @@
/* FOSFENO :: Panel de control
Tema verde acido y negro, con acentos en naranja neon. */
Tema verde acido y negro, con acentos en naranja neon.
Movil: una columna. Pantalla ancha: dos columnas. */
@font-face {
font-family: "Xirod";
src: url("/panel/fonts/xirod.otf") format("opentype");
font-display: swap;
}
:root {
--bg: #060a06;
--card: #0d120c;
@ -27,24 +35,52 @@ body {
padding-bottom: 48px;
}
/* --- Cabecera con el titulo centrado --- */
header {
display: flex; align-items: center; justify-content: space-between;
padding: 18px 20px; border-bottom: 2px solid var(--green-d);
position: sticky; top: 0; background: var(--bg); z-index: 10;
position: sticky; top: 0; z-index: 10;
display: flex; align-items: center; justify-content: center;
padding: 16px 20px; border-bottom: 2px solid var(--green-d);
background: var(--bg);
}
header h1 {
font-size: 20px; font-weight: 800; letter-spacing: 0.32em;
color: var(--green); text-shadow: 0 0 14px rgba(180, 255, 0, 0.55);
font-family: "Xirod", "Arial Black", sans-serif;
font-size: 21px; font-weight: 400; letter-spacing: 0.16em;
color: var(--green); text-shadow: 0 0 16px rgba(180, 255, 0, 0.6);
}
#conn {
position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
}
.dot { width: 13px; height: 13px; border-radius: 50%; display: inline-block; }
.dot.on { background: var(--green); box-shadow: 0 0 9px var(--green); }
.dot.off { background: var(--red); box-shadow: 0 0 9px var(--red); }
/* --- Disposicion: movil en una columna --- */
main {
max-width: 560px; margin: 0 auto;
padding: 16px; display: flex; flex-direction: column; gap: 14px;
}
.column { display: contents; } /* en movil, las columnas no existen */
/* Orden de las tarjetas cuando estan todas en una sola columna (movil) */
#card-power { order: 1; }
#card-engines { order: 2; }
#card-audio { order: 3; }
#card-sens { order: 4; }
#ctl-butterchurn, #ctl-editor, #ctl-mixer, #ctl-projectm { order: 5; }
#card-now { order: 6; }
#card-sys { order: 7; }
#net-foot { order: 8; }
/* --- Disposicion: pantalla ancha (portatil) en dos columnas --- */
@media (min-width: 880px) {
main {
max-width: 1080px; flex-direction: row;
align-items: flex-start; gap: 18px;
}
.column {
display: flex; flex-direction: column; gap: 14px; flex: 1 1 0;
}
}
/* --- Tarjetas --- */
.card {
@ -54,9 +90,10 @@ main {
}
.card.center { align-items: center; }
/* Cabecera de tarjeta: titulo centrado, boton de info a la derecha */
.cardhead {
display: flex; align-items: center; justify-content: space-between;
gap: 10px; width: 100%;
position: relative; width: 100%;
display: flex; align-items: center; justify-content: center;
}
.label { color: var(--green); font-size: 13px; text-transform: uppercase;
@ -68,9 +105,10 @@ main {
gap: 10px; }
.row .cmd { flex: 1; }
/* --- Boton de informacion (circular naranja) --- */
/* --- Boton de informacion (circular naranja, esquina de la tarjeta) --- */
.info {
width: 28px; height: 28px; flex: none; border-radius: 50%;
position: absolute; right: 0; top: 50%; transform: translateY(-50%);
width: 28px; height: 28px; border-radius: 50%;
background: var(--orange); color: var(--ink); border: none;
font-weight: 900; font-style: italic; font-size: 15px; cursor: pointer;
box-shadow: 0 0 10px rgba(255, 122, 0, 0.5);
@ -115,9 +153,7 @@ main {
font-weight: 700; font-size: 14px;
}
.cmd:active, .sysbtn:active { background: #202a16; }
.cmd.accent {
background: var(--green); color: var(--ink); border: none;
}
.cmd.accent { background: var(--green); color: var(--ink); border: none; }
/* --- Selectores y sliders --- */
select {
@ -165,6 +201,8 @@ input[type=range] { width: 100%; accent-color: var(--green); height: 30px; }
font-size: 13px; font-family: "Fira Mono", Consolas, monospace;
}
/* --- Tarjeta de estado --- */
#card-now { text-align: center; }
.now { font-size: 15px; color: var(--orange-n); word-break: break-word; }
/* --- Sistema --- */
@ -172,11 +210,17 @@ input[type=range] { width: 100%; accent-color: var(--green); height: 30px; }
.sys .sysbtn { flex: 1; }
.sysbtn.danger { color: var(--red); border-color: var(--red); }
/* --- Pie con la direccion del panel --- */
.netfoot {
text-align: center; color: var(--dim);
font-size: 12px; font-family: monospace; padding: 4px 0 8px;
}
/* --- Banda de avisos --- */
.notif {
display: flex; align-items: center; gap: 10px;
padding: 12px 16px; font-size: 14px; font-weight: 600;
position: sticky; top: 60px; z-index: 9;
position: sticky; top: 56px; z-index: 9;
}
.notif.info { background: var(--green); color: var(--ink); }
.notif.warn { background: var(--orange); color: var(--ink); }
@ -201,12 +245,8 @@ input[type=range] { width: 100%; accent-color: var(--green); height: 30px; }
box-shadow: 0 0 30px rgba(180, 255, 0, 0.3);
}
.modal-box h2 {
color: var(--green); font-size: 18px; letter-spacing: 0.04em;
font-family: "Xirod", "Arial Black", sans-serif; font-weight: 400;
color: var(--green); font-size: 16px; letter-spacing: 0.06em;
text-align: center;
}
.modal-box p { color: var(--txt); font-size: 14px; line-height: 1.5; }
/* Pie con la direccion del panel */
.netfoot {
text-align: center; color: var(--dim);
font-size: 12px; font-family: monospace; padding: 4px 0 8px;
}