¿Qué tienen en común una Amiga de 1987 y un microcontrolador RP2350 que cuesta menos de diez mil pesos? Que ambos pueden mover en pantalla plasmas, campos de estrellas y esferas cromadas renderizadas en tiempo real. Eso es la demoscene: el arte de exprimir hardware modesto hasta hacerlo brillar. En este tutorial vas a conocer Demofruit, un pack de seis clásicos de la demoscene portados al chip RP2350, y vas a aprender cómo funciona por dentro, cómo cargarlo y cómo empezar a portar estos mismos efectos a una placa que sí consigues en Chile, como la Raspberry Pi Pico 2.

Al terminar vas a entender la arquitectura del proyecto (menú + demos independientes), qué hace cada efecto a nivel técnico, cómo se genera video HDMI desde un microcontrolador y cómo cambiar las texturas y la música por las tuyas.

Menú de arranque de Demofruit mostrando los seis efectos disponibles

Qué es la demoscene (y por qué importa)

La demoscene es una subcultura del arte computacional nacida a comienzos de los 80, cuando los crackers empezaron a agregar pantallas de introducción ("cracktros") a los juegos que liberaban. Con el tiempo esas intros se volvieron un fin en sí mismo: competir por la mejor combinación de programación, gráficos y música corriendo en máquinas limitadísimas. Todavía hoy se juntan en demoparties a mostrar sus producciones.

Demofruit rescata seis de esos efectos icónicos y los hace correr sobre el RP2350. La gracia técnica no es solo que se vean bien, sino que cada uno resuelve un problema distinto de rendimiento en un chip que apenas tiene un puñado de kilobytes de RAM rápida.

El hardware: RP2350 y salida de video

El proyecto original corre sobre la Adafruit Fruit Jam, una placa "mini computador" basada en el RP2350 que trae salida HDMI (DVI-D) y un códec de audio integrados. El corazón es lo importante: el mismo chip RP2350 que encuentras en la Raspberry Pi Pico 2. Ese chip tiene dos núcleos Cortex-M33, y varios de los demos aprovechan los dos: mientras un núcleo renderiza el cuadro, el otro lo escala y lo envía a pantalla.

El video se genera por software usando el periférico PIO del RP2350 para "hablar" HDMI/DVI sin un chip dedicado, una técnica que popularizó el proyecto Pico DVI. La salida es de 320×240 escalada, con un buffer doble para evitar parpadeo. El audio se mezcla en punto flotante y se convierte a 16 bits antes de salir por I2S hacia el códec.

Uno de los efectos de Demofruit corriendo en un monitor CRT

Los seis efectos, explicados

  • Starfield: un campo de estrellas con deformación hacia adelante y texto cromado que rebota letra por letra. Es el clásico "espacio + scroller" con el que abría toda demo ochentera.
  • Plasma: manchas de color que se transforman suavemente, generadas sumando ondas sinusoidales. Usa una tabla de color (LUT) por software para "rotar" la paleta y dar la sensación de movimiento sin recalcular todo.
  • Chrome Sphere: un raytracer que usa los dos núcleos. Una esfera cromada orbita sobre un tablero a cuadros con reflejos, brillos especulares y sombra.
  • Valley Run: un vuelo sobre terreno de malla ("wireframe") con una nave que se inclina al esquivar, guiño directo a los demos de paisajes vectoriales y a Star Fox.
  • Rotozoom: una textura que rota y hace zoom en tiempo real, con una lente esférica encima moviéndose en órbita. Usa matemática de punto fijo para ir rápido.
  • The Juggler: un port del mítico raytracer Juggler de Eric Graham (Amiga, 1987). El original tomaba una hora por cuadro en un 68000 a 7 MHz; el RP2350 renderiza cada cuadro en ~1,5 segundos, los guarda en PSRAM y después los reproduce en loop.

Efecto Valley Run: vuelo sobre terreno wireframe

Instalación: cargar el UF2

Demofruit se distribuye como un archivo UF2 listo para arrastrar. El flujo es el típico de las placas RP2350:

  1. Conecta la placa al computador con un cable de datos USB-C (ojo: los cables de solo carga no sirven, necesitas uno con datos).
  2. Entra al modo bootloader: mantén presionado el botón BOOT/BUTTON1, presiona y suelta Reset sin soltar BOOT, y espera a que aparezca la unidad RP2350 en tu computador. Recién ahí suelta BOOT.
  3. Arrastra el archivo .uf2 a esa unidad. La placa se reinicia sola y Demofruit arranca solo.

Conexiones y controles

Conecta un cable HDMI desde la salida de video de la placa a cualquier TV o monitor con entrada HDMI, y un cable de audio de 3,5 mm hacia parlantes amplificados. Para navegar el menú se usan los botones de la placa:

  • Botón 3 = arriba
  • Botón 2 = abajo
  • Botón 1 = lanzar
  • Reset = salir de un demo y volver al menú

Dentro de cada demo, los tres botones cambian parámetros en vivo: en el Plasma cambian la paleta (Fire, Ice, Toxic, Lava, Rainbow), en el Rotozoom cambian el preset de movimiento, y así.

Detalle de uno de los efectos con paleta de color rotando

Mirar el código por dentro

Toda la lógica vive en un solo sketch de Arduino. La arquitectura es elegante: un menú de arranque, y cada demo aislado en su propio namespace de C++ para que sus variables no choquen. Un reinicio vía el registro "scratch" del watchdog relanza cada demo con la configuración de pantalla ideal. Este es el encabezado y la inicialización de pantalla del sketch principal:

C++
// ═══ DEMOFRUIT — FRUIT JAM DEMOSCENE SAMPLER ═══
// Six demos, one UF2. Boot menu selects; watchdog-scratch reboot launches
// each demo with its ideal display configuration; RESET returns to menu.
//
//   STARFIELD      — warp field + bouncing chrome text   (DVHSTX16)
//   PLASMA         — palette plasma, software palette    (DVHSTX16)
//   CHROME SPHERE  — dual-core raytracer                 (DVHSTX16)
//   VALLEY RUN     — wireframe trench + shaded ship      (DVHSTX16)
//   ROTOZOOM       — texture spin/zoom + lens            (DVHSTX16, dual-core)
//   THE JUGGLER    — Eric Graham 1987, PSRAM playback    (DVHSTX16, dual-core)
//
// REQUIREMENTS: Board = Adafruit Fruit Jam RP2350, CPU Speed = 150MHz.

#include <Arduino.h>
#include <new>
#include <Adafruit_dvhstx.h>
#include <Adafruit_GFX.h>
#include <Fonts/FreeSansBold24pt7b.h>
#include <pico/multicore.h>
#include <hardware/watchdog.h>
#include <math.h>

#include "common.h"
#include "audio.h"
#include "leds.h"

// ── Songs: song_0.h = menu, song_1..6.h = demos in menu order ────────────────
// Each is a standard mod2header output (MOD_DATA), wrapped in a namespace.
namespace song0 {
#include "song_0.h"
}
namespace song1 {
#include "song_1.h"
}
namespace song2 {
#include "song_2.h"
}
namespace song3 {
#include "song_3.h"
}
namespace song4 {
#include "song_4.h"
}
namespace song5 {
#include "song_5.h"
}
namespace song6 {
#include "song_6.h"
}
struct SmpSong { const unsigned char *data; unsigned size; };
static const SmpSong g_songs[7] = {
  { song0::MOD_DATA, sizeof(song0::MOD_DATA) },   // menu
  { song1::MOD_DATA, sizeof(song1::MOD_DATA) },   // starfield
  { song2::MOD_DATA, sizeof(song2::MOD_DATA) },   // plasma
  { song3::MOD_DATA, sizeof(song3::MOD_DATA) },   // chrome sphere
  { song4::MOD_DATA, sizeof(song4::MOD_DATA) },   // valley run
  { song5::MOD_DATA, sizeof(song5::MOD_DATA) },   // rotozoom
  { song6::MOD_DATA, sizeof(song6::MOD_DATA) },   // the juggler
};
#include "texture.h"     // rotozoom bitmap (global: plain data)

// ── Demo modules, each in its own namespace ──────────────────────────────────
namespace sf {
  enum RampPhase { CRUISE, RAMP, WARP, SNAP, RECOVER };
  #include "starfield_impl.h"
}
namespace pl {
#include "plasma_impl.h"
}
namespace rt {
#include "raytracer_impl.h"
}
namespace vy {
#include "valley_impl.h"
}
namespace rz {
#include "rotozoom_impl.h"
}
namespace jg {
#include "juggler_impl.h"
}

#include "launcher.h"

// ── Displays ──────────────────────────────────────────────────────────────────
// DVHSTX16 as a GLOBAL — identical to every proven standalone sketch
// (the library does clock setup tied to static construction). One display,
// one begin() per boot. All demos render through it; plasma uses a
// software palette LUT instead of DVHSTX8 hardware palette (runtime
// display construction proved unreliable).
DVHSTX16 display16(ADAFRUIT_FRUIT_JAM_CFG, DVHSTX_RESOLUTION_320x240,
                   true /* double_buffered */);

static void dispFail() {
  pinMode(LED_BUILTIN, OUTPUT);
  for (;;) {
    Serial.println("FATAL: display.begin() failed");
    digitalWrite(LED_BUILTIN, HIGH); delay(150);
    digitalWrite(LED_BUILTIN, LOW);  delay(850);
  }
}

static DVHSTX16 *use16() {
  DVHSTX16 *d = &display16;
  Serial.println("display16.begin()..."); Serial.flush();
  if (!d->begin()) dispFail();
  Serial.println("display16 OK"); Serial.flush();
  smpPsramFix();
  return d;
}

Fíjate en el arreglo g_songs: cada demo tiene su propia canción MOD incluida como header de C. Ese detalle es clave para personalizar el proyecto.

Personalizar: texturas, música y el easter egg del parlante

Como Demofruit está escrito en Arduino, puedes modificarlo. Dos personalizaciones son directas:

Cambiar la textura del Rotozoom. El demo usa una imagen de 128×128 píxeles guardada en texture.h como arreglo RGB565. El tamaño es potencia de dos a propósito: el demo hace el "wrap" con una máscara de bits (TEXTURE_MASK 0x7F) en vez de una división, que es mucho más rápida. Exporta tu imagen como BMP de 24 bits sin comprimir y conviértela con el script incluido:

Bash
python3 bmp2texture.py yourimage.bmp texture.h
python3 mod2header.py yoursong.mod song_4.h

El segundo comando convierte cualquier archivo .mod de ProTracker al header que espera el sketch, sin alterar la música. Para cambiar la canción de un demo, apunta al song_N.h correspondiente; para el menú, a song_0.h. Puedes bajar MODs libres desde The Mod Archive o componer los tuyos en un tracker como MilkyTracker.

El easter egg del parlante. El conector de parlante de la placa viene muteado por software. Para activarlo, une el pad A1 (GPIO41) a GND con un jumper y reinicia: el estado se lee una sola vez al arranque. El jack de audífonos, en cambio, siempre funciona.

Variantes y mejoras

Estas ideas no están en el tutorial original y son un buen punto de partida para hacer tuyo el proyecto:

  • Portar UN efecto a una Raspberry Pi Pico 2 con display SPI. No necesitas la placa completa para aprender lo importante. El Plasma o el Rotozoom se pueden portar a una Pico 2 (mismo chip RP2350) manejando un display SPI genérico (ILI9341, ST7789). Es la mejor forma de entender el bucle de render y la matemática de punto fijo sin depender de HDMI.
  • Agregar un demo propio. La arquitectura de namespaces hace trivial sumar un séptimo efecto: crea tu mi_demo_impl.h, envuélvelo en un namespace y agrégalo al menú. Un buen primer proyecto es un "tunnel" o un "metaballs".
  • Reactividad al audio. Como la música MOD ya se mezcla en el núcleo 0, puedes leer la amplitud del canal y usarla para modular, por ejemplo, la intensidad del warp del Starfield o la frecuencia del Plasma, y tener visuales que reaccionan a la música.

Personalización para Chile

La Adafruit Fruit Jam no se vende en Chile, pero el chip que la hace interesante. el RP2350. sí. En MechatronicStore consigues todo lo necesario para empezar a experimentar con estos efectos localmente:

  • Raspberry Pi Pico 2 (RP2350) (SKU N-340). $9.990 CLP. Mismo chip de doble núcleo que la Fruit Jam. Es la placa ideal para portar los efectos a un display SPI; no corre el UF2 de Demofruit tal cual (la Fruit Jam trae HDMI y códec de audio integrados que la Pico no tiene), pero sí todo el "cerebro" de los demos.
  • Cable HDMI Dorado Macho Macho 1.8 m (SKU F-512). $3.990 CLP. Para la salida de video del build original hacia tu TV o monitor.
  • Cable USB-C a USB-A 1 m (SKU B-101). $2.190 CLP. Cable de datos para cargar el firmware y alimentar la placa desde el PC o un cargador de 5V.

Para el audio no necesitas parlantes USB de marca: cualquier parlante amplificado con entrada de 3,5 mm sirve. Si quieres armarlo tú mismo, un módulo amplificador PAM8403 con un parlante de 8Ω hace el trabajo por poco más de mil pesos. Como pantalla, cualquier TV o monitor con entrada HDMI que ya tengas en casa funciona.

Recursos

  • Tutorial original: Demofruit: Demoscene for Fruit Jam (Adafruit Learning System, por John Park)
  • MODs libres para la música: The Mod Archive
  • Tracker para componer: MilkyTracker (macOS, Windows y Linux)
  • Código: el sketch principal en Arduino viene incluido más arriba y en el Project Bundle del tutorial original.

Versión chilena, inspirada en el proyecto de Adafruit, con componentes RP2350 en stock local en MechatronicStore.