Repository navigation
v2.1.1
ReaSet Changelog
English
v2.1.1 — Playback & Native Loop Fixes
March 27, 2026
Bug fixes
-
Audio glitch at region end with auto-stop enabled — When region A's endpoint matched region B's startpoint, the last ~200ms of A would play back 2–3 times before REAPER processed the stop. Root cause: the transport tick (~33ms) fired the end-of-region command multiple times before receiving a response. Fixed with three coordinated changes:
- ID-based deduplication (
_lastRegionEndTrigger): the trigger fires only once per region and re-arms when the playhead re-enters the region. - Time-based lock (
_endOfRegionLocked, 400ms): blocks re-triggers while REAPER processes the async stop. Applied across all end-of-region paths:autoStop,shouldPlayNext, andstopAfter. - Tighter detection window (
timeRem > -0.1instead of> -1): eliminates false positives when the cursor briefly lands just past the region endpoint.
- ID-based deduplication (
-
delayAfterpath missing lock protection — ThedelayAfteroverride sent a STOP but did not activate_endOfRegionLocked, allowing re-triggers during the configured pause when the next region was adjacent. Fixed with the same lock logic as the other paths. -
Native REAPER loop was incorrectly cancelled —
_reaperNativeLoopOff()was called unconditionally before checkingactiveRegion.loop, tearing down the native loop (Repeat + A/B points) and falling back to the internal JS loop instead. Fixed: ifReaSet_NativeLoop.lua's native loop is active for the current region (_nativeLoopSubId === activeRegion.id), the Lua script already handles the jump and JS no longer intervenes. -
Stutter + stop when clicking a song with auto-stop enabled — Race condition between the user's click XHR and a stale pending transport response. When a song item was clicked,
playRegiondispatchedSET/POS + PLAYimmediately. If a transport response arrived shortly after showing the old cursor position near the previous song's end (chain=false), the autoStop block dispatched aSTOPthat REAPER received after thePLAY, killing the new playback. Fixed with the_playRegionLockedflag (500ms): set byplayRegionon every user-initiated play, checked by the autoStop block before sending itsSTOP. The 100ms repositioningsetTimeoutis also cancelled if the flag is still active. -
Stutter + stop persistent:
triggerMidi()internal STOP kills user playback — The true root cause of the bug. When the previous song's autoStop fires, its 100ms setTimeout callstriggerMidi()to pre-arm MIDI for the next song.triggerMidi()sends PLAY and schedules a STOP 100ms later. If the user clicks a song during that 100ms window,triggerMidi()'s internal STOP arrives at REAPER after the user's PLAY, killing their playback. Fixed by adding the_playbackInitiatedflag (500ms), set exclusively byplayRegion(), whichtriggerMidi()checks before sending its internal STOP. A separate flag from_playRegionLockedis required becausecueRegion()also sets_playRegionLockedand legitimately needstriggerMidi()'s STOP to fire (to stop REAPER after the brief MIDI-arm play). -
cueRegionunprotected against autoStop — The cue function (pre-position without playing) did not set_playRegionLocked, so if a stale transport response arrived while the cue was in progress, the autoStop block could send a spuriousSTOPor fire its 100ms repositioningsetTimeout, overwriting the freshly cued position or triggering a duplicatetriggerMidi. The same_playRegionLocked(500ms) guard applied toplayRegionis now also applied tocueRegion. -
Boundary marker assigned to both adjacent songs — When a marker was placed exactly at the junction where one song's endpoint coincides with the next song's startpoint, the range check
[song.start - ε, song.end + ε]would include it in both songs, generating duplicate nested sections. Implemented_markerBelongsToSong()with semantic disambiguation: (1) if the marker's display name matches a candidate song's display name (case-insensitive), it belongs to that song; (2) "end" keywords (end,fin,outro,salida,cierre,coda,fade, etc.) → belongs to the song ending there; (3) "start" keywords (start,inicio,intro,entrada,apertura,begin, etc.) → belongs to the song starting there; (4) no keyword match → defaults to the song starting there (a boundary marker without exit-keywords most likely names the incoming section).
New file
Requirements/ReaSet_NativeLoop.lua— Persistent background script for REAPER. Listens to theReaSet/nativeLoopExtState every frame and, on signal, creates a visual region in the timeline, sets native A/B loop points, and enables Repeat. Detects playhead crossings and, after the configured number of passes (loopMax, 0 = infinite), disables Repeat and removes the region. Announces itself to ReaSet automatically viaReaSet/nativeLoopReady=1.
v2.1 — Display, Loop & Command System Overhaul
March 2026
New features
- Real-time display filters — New "Display Filters" section in the sidebar with independent sliders to adjust Luminance (50–150%), Contrast (50–150%), and Saturation (0–200%) for the setlist body. Values are saved automatically and restored when the app restarts.
- Fractional loop counter — When a section has
+LOOP:Nactive, a badge shows the current pass as a fraction (e.g.2/4) to the left of the song action buttons, in the corresponding nested section row, and in Live View next to the section name. - Context menu in nested sections — Nested section rows now have their own 3-dot menu (to the left of the name) with access to Notes and Color selection, using the same ID-based override system as songs.
- Next section indicator in Live — Live View shows the next nested section to the right of the active name, separated by an arrow
→. If no section is active but there is an upcoming one, it is also shown as a preview. - Secondary progress bar in Live — A second progress bar, thinner and shorter than the main one, reflects progress inside the active nested section. The fill color matches the color assigned to that section in the setlist body.
- Command system in region names — Region and marker names in REAPER now support a full inline command system to control behavior, appearance, and duration directly from the project, without touching the app. See command reference below.
UI improvements
- Higher visual contrast — All text, borders, badges, and card backgrounds in the setlist body increased luminance and opacity by 20%. Global variables
--text-secondaryand--text-subduedwere updated accordingly. The transport bar and app background remain unchanged. - Live progress bar is now white — The main progress bar fill in Live View changed from green to pure white for better on-stage readability.
- Drag handle and 3-dot button redesign — The handle has greater weight and contrast. The 3-dot button is now vertical (three SVG dots), with no circular background, and a vertical divider between both elements.
- SVG icons in context menu — Song options menu icons (Stop, Timer, Notes, Color) replaced emojis with SVG icons consistent with the dark theme.
Bug fixes
- Context menu did not close when clicking outside — The
removeEventListenerlistener was executed unconditionally on every click, preventing the menu from closing after the first external interaction. Fixed. +LOOP:Nplayed N+1 times — MultipleupdatePlaybackUIticks within the 200ms window before REAPER responded toSET/POScaused duplicate loop-counter increments. Fixed with a dedupe guard_lastLoopFireKeykeyed bysectionId:endPos.- Real-time loop changes were not applied — The
_loopInitflag permanently blocked re-applying default loop state when detecting changes in region name. Replaced with_loopDefault, which tracks the last parsed value and applies changes immediately. - Setlists were not isolated per project — Override and setlist data in localStorage was shared across different REAPER projects. A per-project fingerprint (djb2 hash of sorted region IDs) was implemented as a suffix for all storage keys.
- Keyboard shortcuts blocked typing in notes — Letter keys mapped to app functions were intercepting input while focus was in a
<textarea>. Fixed with atagNameguard. Additionally,Entersaves and removes focus, andEscapeonly removes focus. - Secondary Live progress color did not match setlist color — The bar used the region raw color; it now goes through the same
getClosestPaletteColor()pipeline as the body, ensuring both colors are identical.
v2.1 Command Reference
Commands are written directly in the region or marker name in REAPER. They can be combined in any order. The clean name left after parsing all commands is what is displayed in the app.
Combined example:
Chorus {dense pre-chorus} +LOOP:4 [green] [.bold] [1:20]
+ commands — Playback behavior
| Command | Description |
|---|---|
+PAUSE |
Pauses playback when reaching the end of the section. |
+SKIP |
Marks the section as skipped by default. It appears crossed out and with reduced opacity. |
+LOOP |
Enables infinite looping for the section. |
+LOOP:N |
Repeats the section exactly N times, then continues. The X/N badge is shown in real time. |
+LOOPFULL |
Loop with absolute priority: if a song is queued, it waits for the loop to finish before jumping. |
Brackets [] — Appearance and duration
| Command | Description |
|---|---|
[color] |
Assigns a palette color to the card. See available colors below. |
[mm:ss] |
Overrides the displayed section duration (minutes:seconds format). |
[nosong] |
Excludes the item from numbering and total song count. Appears with reduced opacity. |
[.class] |
Applies a custom CSS class to the name. See available classes below. |
Available colors:
gray · red · orange · amber · yellow · lime · green · emerald · teal · cyan · sky · blue · indigo · violet · purple · fuchsia · pink · rose
Available CSS classes:
| Class | Effect |
|---|---|
.bold |
Name in extra bold (900) with adjusted tracking. |
.dim |
Name at 42% opacity. |
.italic |
Name in italic. |
.loud |
Name in uppercase with expanded spacing and maximum weight. |
Braces {} — Informational text
| Command | Description |
|---|---|
{text} |
Displays helper text in italic next to the section name. Not shown in Live View or Canvas. Useful for production notes visible in the setlist. |
Special prefixes — Markers
| Command | Description |
|---|---|
> |
Converts the marker into a section marker (sub-region of the active song). |
* |
Completely ignores the marker; it does not appear in the app. |
>>> DestinationName |
When the section ends, automatically jumps to the region whose name matches DestinationName. |
Reserved words
| Name | Description |
|---|---|
STOP |
Stop marker. Ends playback when reached. |
SONG END |
Alias of STOP. |
v2.0 — Nested regions and override system
December 2025
- Full support for nested regions (sub-sections) with rendering in the setlist body and Live View
- ID-based override system (
g_songOverrides) for custom notes and colors per song and section - Live View redesigned with song name, active region, progress bar, and transport controls
- Canvas Mode for on-stage projection
- Themed color palette with automatic mapping (
getClosestPaletteColor) - Grid View mode for quick navigation
- Support for special markers:
+STOP,+PAUSE,+LOOP,+SKIP,[nosong] - Export/Import of all app data in JSON
v1.0 — First release
August 2025
- Web setlist manager for REAPER with WebSocket connection
- Song play/stop from the UI
- Drag & drop reordering
- Real-time playback position sync
- Multi-setlist support with localStorage persistence
Español
v2.1.1 — Correcciones de reproducción y loop nativo
27 de marzo de 2026
Corrección de bugs
-
Glitch de audio al final de región con auto-stop activado — Cuando el endpoint de la región A coincidía con el startpoint de la región B, el último ~200ms de A se repetía 2–3 veces antes de que REAPER procesara el stop. Causa raíz: el tick de transporte (cada ~33ms) disparaba el comando de fin de región múltiples veces antes de recibir respuesta. Resuelto con tres cambios coordinados:
- Deduplicación por ID (
_lastRegionEndTrigger): el trigger solo se activa una vez por región y se re-arma al volver a entrar a la región. - Lock temporal (
_endOfRegionLocked, 400ms): bloquea re-disparos mientras REAPER procesa el stop asíncrono. Aplicado en todos los paths de fin de región:autoStop,shouldPlayNextystopAfter. - Ventana de detección acotada (
timeRem > -0.1en vez de> -1): elimina falsos positivos cuando el cursor queda brevemente pasado el endpoint de la región.
- Deduplicación por ID (
-
Path
delayAftersin lock de protección — El overridedelayAfterenviaba el STOP pero no activaba_endOfRegionLocked, lo que permitía re-disparos durante la pausa configurada cuando la región siguiente era adyacente. Corregido con la misma lógica de lock que los demás paths. -
Loop nativo de REAPER se cancelaba incorrectamente — La llamada a
_reaperNativeLoopOff()ocurría incondicionalmente antes de verificaractiveRegion.loop, eliminando el loop nativo (Repeat + A/B points) y cayendo al loop JS interno en su lugar. Corregido: si el native loop deReaSet_NativeLoop.luaestá activo para la región actual (_nativeLoopSubId === activeRegion.id), el script Lua ya gestiona el jump y JS no interviene. -
Stutter + stop al hacer click en una canción con auto-stop activado — Race condition entre el XHR del click del usuario y una respuesta de transport pendiente con posición antigua. Al hacer click en una canción,
playRegiondespachabaSET/POS + PLAYinmediatamente. Si en ese mismo momento llegaba una respuesta de transport mostrando la posición anterior cerca del final de la canción previa (chain=false), el bloque autoStop despachaba unSTOPque REAPER recibía después delPLAY, deteniendo el playback. Resuelto con el flag_playRegionLocked(500ms): al hacer click se activa, y el bloque autoStop lo verifica antes de enviar su STOP. También se cancela elsetTimeoutde reposicionamiento de 100ms si el flag sigue activo. -
Stutter + stop persistente: STOP interno de
triggerMidi()mata el playback del usuario — Causa raíz real del bug. Cuando el autoStop de la canción anterior dispara, su setTimeout de 100ms llama atriggerMidi()para pre-armar el MIDI de la siguiente canción.triggerMidi()envía PLAY y programa un STOP 100ms después. Si el usuario hace click en una canción durante esa ventana de 100ms, el STOP interno detriggerMidi()llega a REAPER después del PLAY del usuario, deteniendo el playback. Corregido añadiendo el flag_playbackInitiated(500ms) seteado exclusivamente porplayRegion(), quetriggerMidi()verifica antes de enviar su STOP interno. Se usa un flag separado de_playRegionLockedporquecueRegion()también setea ese flag y necesita que el STOP detriggerMidi()se ejecute normalmente (para detener el arm de MIDI tras el cue). -
cueRegionsin protección contra autoStop — La función de cue (preposicionar sin reproducir) no activaba_playRegionLocked, por lo que si llegaba una respuesta de transport estancada con posición antigua mientras el cue estaba en curso, el bloque autoStop podía enviar unSTOPadicional o disparar susetTimeoutde reposicionamiento, sobreescribiendo la posición de cue recién establecida o ejecutando untriggerMididuplicado. Aplicado el mismo guard de_playRegionLocked(500ms) acueRegion. -
Marcador en límite entre canciones asignado a ambas — Cuando un marcador estaba ubicado exactamente en el punto donde el endpoint de una canción coincide con el startpoint de la siguiente, el check de rango
[song.start - ε, song.end + ε]lo incluía en las dos canciones a la vez, generando secciones duplicadas. Implementada la función_markerBelongsToSong()con asignación semántica: (1) si el nombre del marcador coincide con el nombre de display de una de las canciones candidatas, pertenece a esa canción; (2) palabras clave de fin (end,fin,outro,salida,cierre,coda,fade, etc.) → pertenece a la canción que termina ahí; (3) palabras clave de inicio (start,inicio,intro,entrada,apertura,begin, etc.) → pertenece a la canción que comienza ahí; (4) sin coincidencia de palabras clave → se asigna a la canción que comienza (por defecto, un marcador en un límite sin palabras clave de salida probablemente nombra la sección entrante).
Nuevo archivo
Requirements/ReaSet_NativeLoop.lua— Script de fondo persistente para REAPER. Escucha el ExtStateReaSet/nativeLoopcada frame y, al recibir la señal, crea una región visual en el timeline, configura los puntos A/B del loop nativo y activa Repeat. Detecta los cruces del playhead y, tras el número de repeticiones configurado (loopMax, 0 = infinito), desactiva Repeat y elimina la región. Se anuncia automáticamente a ReaSet viaReaSet/nativeLoopReady=1.
v2.1 — Overhaul de display, loop y sistema de comandos
Marzo 2026
Nuevas funciones
- Filtros de pantalla en tiempo real — Nueva sección "Display Filters" en la sidebar con sliders independientes para ajustar Luminancia (50–150%), Contraste (50–150%) y Saturación (0–200%) del cuerpo del setlist. Los valores se guardan automáticamente y se recuperan al reiniciar la app.
- Contador de loops fraccionario — Cuando una sección tiene
+LOOP:Nactivo, aparece un badge con la vuelta actual en formato fracción (ej.2/4) a la izquierda de los botones de acción de la canción, en la fila de la sección anidada correspondiente, y en la vista Live junto al nombre de la sección. - Menú contextual en secciones anidadas — Las filas de secciones anidadas ahora tienen su propio menú de 3 puntos (a la izquierda del nombre) con acceso a Notas y selección de Color, usando el mismo sistema de overrides por ID que las canciones.
- Indicador de sección siguiente en Live — La vista Live muestra la próxima sección anidada a la derecha del nombre activo, separada por una flecha
→. Si no hay ninguna sección activa pero hay una próxima, también se muestra como anticipo. - Barra de progreso secundaria en Live — Una segunda barra de progreso, más delgada y corta que la principal, refleja el avance dentro de la sección anidada activa. El color de relleno coincide con el color asignado a esa sección en el cuerpo del setlist.
- Sistema de comandos en nombres de región — Los nombres de regiones y marcadores en REAPER ahora admiten un sistema completo de comandos inline para controlar comportamiento, apariencia y duración directamente desde el proyecto, sin tocar la app. Ver referencia de comandos más abajo.
Mejoras de interfaz
- Contraste visual elevado — Todos los textos, bordes, badges y fondos de tarjetas del cuerpo del setlist aumentaron su luminancia y opacidad en un 20%. Variables globales
--text-secondaryy--text-subduedactualizadas en consecuencia. La barra de transporte y el fondo de la app permanecen sin cambios. - Barra de progreso en Live ahora blanca — El relleno de la barra de progreso principal en Live View cambió de verde a blanco puro para mayor legibilidad en escena.
- Rediseño del drag handle y botón 3-dot — El handle tiene mayor peso y contraste. El botón 3-dot ahora es vertical (tres puntos SVG), sin fondo circular, con separador vertical entre ambos elementos.
- Iconos SVG en el menú contextual — Los iconos del menú de opciones de canción (Stop, Timer, Notas, Color) reemplazaron a los emojis por SVGs coherentes con el tema oscuro.
Corrección de bugs
- Menú contextual no se cerraba al hacer click fuera — El listener
removeEventListenerse ejecutaba incondicionalmente en cada click, lo que impedía que el menú cerrara tras la primera interacción externa. Corregido. - +LOOP:N reproducía N+1 veces — Múltiples ticks de
updatePlaybackUIdentro de la ventana de 200ms antes de que REAPER respondiera alSET/POScausaban un incremento duplicado del contador de loops. Resuelto con un guard de deduplicación_lastLoopFireKeyclave porsectionId:endPos. - Cambios de loop en tiempo real no se aplicaban — El flag
_loopInitbloqueaba permanentemente la re-aplicación del estado por defecto del loop al detectar cambios en el nombre de la región. Reemplazado por_loopDefaultque rastrea el último valor parseado y aplica el cambio de inmediato. - Setlists no aisladas por proyecto — Los datos de overrides y setlists en localStorage se compartían entre proyectos de REAPER distintos. Implementado fingerprint por proyecto (hash djb2 de IDs de región ordenados) como sufijo de todas las claves de almacenamiento.
- Atajos de teclado bloqueaban la escritura en notas — Las teclas de letras asociadas a funciones de la app interceptaban el input mientras el foco estaba en un
<textarea>. Corregido con guard portagName. Adicionalmente,Enterguarda y quita el foco, yEscapesolo quita el foco. - Color del progress secundario en Live no coincidía con el setlist — La barra usaba el color raw de la región; ahora pasa por el mismo pipeline
getClosestPaletteColor()que el cuerpo, garantizando que ambos colores sean idénticos.
Referencia de comandos v2.1
Los comandos se escriben directamente en el nombre de la región o marcador en REAPER. Se pueden combinar en cualquier orden. El nombre limpio que queda después de parsear todos los comandos es el que se muestra en la app.
Ejemplo combinado:
Chorus {pre-coro denso} +LOOP:4 [green] [.bold] [1:20]
Comandos + — Comportamiento de reproducción
| Comando | Descripción |
|---|---|
+PAUSE |
Pausa la reproducción al llegar al final de la sección. |
+SKIP |
Marca la sección como omitida por defecto. Aparece tachada y en opacidad reducida. |
+LOOP |
Activa el loop infinito de la sección. |
+LOOP:N |
Repite la sección exactamente N veces y luego continúa. El badge X/N se muestra en tiempo real. |
+LOOPFULL |
Loop con prioridad absoluta: si hay una canción en cola, espera a que el loop termine antes de saltar. |
Corchetes [] — Apariencia y duración
| Comando | Descripción |
|---|---|
[color] |
Asigna un color de la paleta a la tarjeta. Ver colores disponibles abajo. |
[mm:ss] |
Sobreescribe la duración mostrada de la sección (formato minutos:segundos). |
[nosong] |
Excluye el elemento de la numeración y el conteo total de canciones. Aparece en opacidad reducida. |
[.clase] |
Aplica una clase CSS personalizada al nombre. Ver clases disponibles abajo. |
Colores disponibles:
gray · red · orange · amber · yellow · lime · green · emerald · teal · cyan · sky · blue · indigo · violet · purple · fuchsia · pink · rose
Clases CSS disponibles:
| Clase | Efecto |
|---|---|
.bold |
Nombre en negrita extra (900) con tracking ajustado. |
.dim |
Nombre al 42% de opacidad. |
.italic |
Nombre en cursiva. |
.loud |
Nombre en mayúsculas con espaciado ampliado y peso máximo. |
Llaves {} — Texto informativo
| Comando | Descripción |
|---|---|
{texto} |
Muestra texto auxiliar en cursiva junto al nombre de la sección. No se muestra en Live View ni en Canvas. Útil para notas de producción visibles en el setlist. |
Prefijos especiales — Marcadores
| Comando | Descripción |
|---|---|
> |
Convierte el marcador en un marcador de sección (sub-región de la canción activa). |
* |
Ignora completamente el marcador; no aparece en la app. |
>>> NombreDestino |
Al finalizar la sección, salta automáticamente a la región cuyo nombre coincida con NombreDestino. |
Palabras reservadas
| Nombre | Descripción |
|---|---|
STOP |
Marcador de parada. Finaliza la reproducción al alcanzarlo. |
SONG END |
Alias de STOP. |
v2.0 — Regiones anidadas y sistema de overrides
Diciembre 2025
- Soporte completo de regiones anidadas (sub-sections) con visualización en el cuerpo del setlist y la vista Live
- Sistema de overrides por ID (
g_songOverrides) para notas y colores personalizados por canción y sección - Vista Live rediseñada con nombre de canción, región activa, barra de progreso y controles de transporte
- Canvas Mode para proyección en escena
- Paleta de colores temática con mapeo automático (
getClosestPaletteColor) - Modo Grid View para navegación rápida
- Soporte de marcadores especiales:
+STOP,+PAUSE,+LOOP,+SKIP,[nosong] - Export/Import de todos los datos de la app en JSON
v1.0 — Primera versión
Agosto 2025
- Setlist manager web para REAPER con conexión WebSocket
- Reproducción/parada de canciones desde la interfaz
- Reordenamiento por drag & drop
- Sincronización de posición de reproducción en tiempo real
- Soporte multi-setlist con persistencia en localStorage