Novità di prodotto
Il PanelWave Player è uscito

Il player Angular con licenza MIT è su npm: navigazione a grafo con uno stato vero, tre viste (compresa la tela infinita), vignette video, il renderer ComicBalloon, hotspot e varianti funzionanti, regole di paywall con verifica dell’età, un tracciamento che rispetta il consenso e una sandbox per i plugin. Ecco cosa contiene, come integrarlo e cosa manca ancora.
Il PanelWave Player è la metà aperta dell’ecosistema: una libreria Angular con licenza MIT (Angular 20, 21 e 22) che prende un manifest PanelWave e lo trasforma in un’esperienza di lettura, con navigazione, localizzazione, media e controlli per il lettore inclusi. È sul registro pubblico di npm come @panelwave/player, oggi alla versione 1.2.0, e si installa con npm install @panelwave/player. Tutte le fasi di sviluppo delle funzionalità principali sono concluse e gli unit test passano tutti. Una suite end-to-end Playwright con controlli di accessibilità axe-core gira su browser desktop e mobile, e la documentazione del player descrive la libreria per come si comporta davvero, imperfezioni comprese.
Il codice sorgente è su github.com/panelwave/player, e una demo dal vivo fa girare le storie di esempio nel tuo browser. In questo post facciamo il giro di ciò che il player contiene, lo integriamo in cinque minuti e vediamo i punti di integrazione che interessano a chi lo ospita e le novità della prossima release. In chiusura, un elenco onesto di ciò che non è finito. Se ti interessa solo l’API, vai direttamente a Inputs and Outputs.
Che cosa riproduce il player
Un’opera è un insieme di capitoli le cui vignette sono nodi di un grafo orientato. Il player percorre quel grafo valutando le condizioni JSON Logic sui collegamenti, e compone ogni vignetta dai suoi livelli: immagini con caricamento lazy e cache, livelli video con modalità di riproduzione e trigger di avvio, livelli di testo e di plugin in sandbox, e balloon disegnati dal motore ComicBalloon. Nella vista vignetta le vignette si leggono una alla volta, nella vista pagina sono composte in tavole, nella vista tela si attraversano su un piano continuo. La release 1.2.0 legge il formato PanelWave fino alla versione 1.6; la panoramica ha la tabella completa delle funzionalità.
Navigazione e stato che si ramificano davvero
La ramificazione non è una modalità speciale. Il motore di flusso raccoglie i collegamenti in uscita dalla vignetta corrente, ne valuta le condizioni rispetto all’archivio delle variabili e, tra quelli validi, sceglie il collegamento con la priorità più alta. Quando il lettore percorre un collegamento, si applicano le modifiche alle variabili che porta con sé; tornando indietro si ripristinano sia la posizione sia lo stato. Se sono aperti più percorsi, il pulsante Choices della barra degli strumenti lascia scegliere al lettore, mostrando per ciascuno il testo del collegamento. Le variabili vivono in cinque ambiti (global, chapter, page, session, persistent), sono tipizzate e validate a ogni scrittura, e l’ambito persistent sopravvive ai ricaricamenti grazie a localStorage. Nel dubbio, una condizione chiude il passaggio: un errore di valutazione conta come false, e un collegamento senza condizione è sempre percorribile. I meccanismi di runtime sono documentati passo per passo in State, Conditions and Navigation, i dettagli sulla memorizzazione in Saving Progress.
Le definizioni delle variabili del manifest sono collegate all’archivio, quindi valori predefiniti, tipi, intervalli e flag di sola lettura valgono fin dalla prima vignetta. La finestra delle impostazioni ha poi una scheda Variables: le variabili marcate come pubbliche compaiono con un editor adatto al loro tipo, così un autore può lasciare di proposito ai lettori il controllo di alcuni interruttori della storia (Settings modal).
Tre viste, compresa la tela infinita
La vista vignetta e la vista pagina ci sono fin dalle prime fasi. La vista tela, arrivata con il formato 1.4, colloca tutte le vignette di un capitolo su un unico piano nello spazio del mondo e fa scivolare una camera dall’una all’altra lungo il grafo narrativo, con percorsi diretti, ad arco o a waypoint definiti dall’autore e profili di zoom hold, pull-back o dive. Qualsiasi input del lettore interrompe lo spostamento, come in un’app di mappe. Il player monta solo le vignette vicine alla camera, al massimo dodici, per quanto grande sia il capitolo, e le modalità di rivelazione tengono nascosti i rami segreti finché il percorso non li raggiunge. La semantica della navigazione non cambia: «avanti» interroga sempre il motore di flusso. Dettagli in Canvas View e nel riferimento del formato.
Vignette video e un motore audio a quattro bus
I livelli video supportano le modalità di riproduzione once, loop e ping-pong, i trigger di avvio alla visualizzazione e al clic, e, nella vista pagina, la riproduzione in sequenza: i video di una tavola partono uno dopo l’altro nell’ordine di lettura. La riproduzione automatica tiene conto dei video: invece di seguire un timer fisso, aspetta che il video arrivi alla fine almeno una volta, e se una sorgente si blocca un watchdog fa avanzare comunque. Non suona mai più di un video con l’audio attivo alla volta. Il motore audio mixa quattro bus (ambiente, musica, voce fuori campo, effetti sonori) con volume e dissolvenze indipendenti, e rispetta le regole dei browser sulla riproduzione automatica. Vedi Audio, il riferimento del formato video e Autoplay.
Il renderer ComicBalloon
I balloon sono veri balloon da fumetto, disegnati in SVG da una classe TypeScript indipendente da qualsiasi framework: il testo viene misurato, il corpo è uno squircle (oppure una nuvoletta di pensiero, un’esplosione per l’urlo, un sussurro tratteggiato, un riquadro del narratore ad angoli vivi, forme di didascalia con angoli tagliati o un connettore), la coda è integrata nel contorno, e il risultato si aggiorna con chiamate leggere. Dieci tipi di balloon, ondulazioni e punte deterministiche, un effetto che nasconde il bordo per i balloon che si fondono con il margine della vignetta, e font da lettering con licenza aperta inclusi nel pacchetto. Lo stile si risolve attraverso una cascata: impostazioni predefinite dell’opera, personalizzazione per personaggio, preset con nome e personalizzazione del singolo balloon. Il CMS integra lo stesso renderer, quindi il lettering che l’autore vede nell’editor è quello che vedono i lettori. Balloon Renderer spiega la pipeline; Speech Bubbles copre il lato del manifest.
Hotspot e varianti di vignetta in funzione
Gli hotspot disegnati dall’autore funzionano nel player: le aree rettangolari, circolari e poligonali diventano zone cliccabili con un leggero contorno pulsante (disattivato con il movimento ridotto), e attivandone una si esegue la sua azione: andare a una vignetta con una transizione, impostare variabili, aprire gli extra su un elemento specifico, mostrare una finestra modale localizzata o passare l’evento a un plugin. Gli hotspot sono accessibili da tastiera e annunciano le proprie etichette agli screen reader, e ogni clic viene registrato come evento di tracciamento con coordinate normalizzate: è ciò che alimenta la heatmap dei clic del CMS. Anche le varianti di vignetta si risolvono a runtime: la prima variante la cui condizione è vera sostituisce il contenuto della vignetta, e la scelta viene rivalutata a ogni cambio di variabile. Se un’opera offre alternative, il pulsante Alt della barra degli strumenti le fa scorrere. Vedi Interactive hotspots e Variants.
Localizzazione senza ricaricare
Ogni testo destinato ai lettori è una LocalizedString indicizzata per locale BCP-47, e gli asset possono avere varianti per lingua. Il player risolve entrambi attraverso una catena di fallback (locale esatta, lingua di base, lingua predefinita dell’opera, la sua lingua di base, prima disponibile) e cambia lingua all’istante dalla finestra della lingua nella barra degli strumenti o con il metodo changeLocale. L’interfaccia del player si traduce a parte, con file ngx-translate forniti dalla tua app; inglese e tedesco sono già nel pacchetto. Localization copre entrambi i livelli e la catena.
Prestazioni su reti lente e dispositivi modesti
Tre meccanismi reggono il carico. Una coda di precaricamento a priorità, con concorrenza limitata, si regola in base alla Network Information API, scende a zero quando il lettore ha attivato il risparmio dati e carica in anticipo le illustrazioni delle vignette raggiungibili da quella corrente. Una cache di immagini LRU decodifica fuori dal thread principale con createImageBitmap entro un budget di 100 MB. Infine, per ogni vignetta, vista e livello di zoom, il player sceglie la variante d’immagine più piccola che copre la larghezza effettiva di visualizzazione, arrotondata a passi di 256 pixel e solo verso l’alto: chi legge su uno smartphone scarica le varianti WebP piccole, chi fa zoom su uno schermo 4K riceve la codifica completa. Tutti i componenti usano OnPush. Tutto il meccanismo è descritto nella pagina Performance.
Accessibilità di serie
Ogni pulsante della barra degli strumenti ha un aria-label tradotto e gli interruttori espongono aria-pressed. Il viewport è una regione etichettata attivabile da tastiera, le vignette nella vista pagina possono ricevere il focus e gli hotspot sono nell’ordine di tabulazione. Il movimento ridotto si attiva quando lo chiede il sistema operativo, l’interruttore del lettore nella finestra delle impostazioni o l’input reducedMotion dell’host: disattiva le animazioni di transizione e trasforma l’avvio dei video alla visualizzazione in un avvio al clic. La suite Playwright esegue controlli axe-core nella CI. Vedi Accessibility.
Paywall, tracciamento e plugin
Il player non elabora mai pagamenti. Il manifest dichiara che cosa è protetto, per opera, capitolo, vignetta o blocco di extra; il player valuta queste regole rispetto a uno snapshot dei diritti di accesso che la tua app gli passa o che il player stesso scarica una volta all’avvio dal tuo endpoint. Chi arriva a un contenuto bloccato vede un overlay del paywall con le opzioni Buy e Subscribe della regola, le vignette di anteprima gratuita restano aperte, e una verifica dell’età integrata nel player copre i contenuti con classificazione per età; la scelta del lettore viene restituita alla tua app tramite l’output paywallAction. Se vuoi che sia il tuo servizio a decidere vignetta per vignetta, c’è anche un adattatore dei diritti di accesso. Paywall and Entitlement spiega quale integrazione fa al caso tuo.
Il tracciamento nasce rispettoso del consenso: se il manifest lo richiede, non si registra nulla prima che il lettore l’abbia dato. Si conservano solo i tipi di evento ammessi, gli eventi ad alta frequenza vengono raggruppati e i lotti partono verso l’endpoint indicato nel manifest. Chi ospita il player può anche fare a meno del servizio e inoltrare gli output della shell al proprio sistema di analisi. Vedi Tracking.
I plugin sono normali pagine web in iframe in sandbox che comunicano con l’host tramite postMessage, con un modello di permessi basato su dodici capacità, stati del ciclo di vita e un protocollo documentato. Le vignette possono anche incorporare direttamente contenuti di plugin come livello plugin, per esempio un visualizzatore a 360 gradi. Vedi Plugins.
Integralo in cinque minuti
Il componente da integrare è la shell del player. È standalone, quindi non c’è alcun modulo da importare: aggiungi provideHttpClient e un loader TranslateModule alla configurazione della tua app, includi una volta il foglio di stile dei font per i balloon e indica alla shell l’URL del tuo manifest (oppure recupera tu il JSON e passalo come oggetto). Un esempio completo e funzionante con un manifest minimo valido è in Quickstart: Embed.
<pw-player-shell [manifestUrl]="manifestUrl" [locale]="'en-US'" [showToolbar]="true" [entitlementSnapshot]="snapshot" (ready)="onReady()" (panelChange)="savePosition($event)" (paywallAction)="onPaywall($event)" (localeChange)="onLocale($event)"> </pw-player-shell>
Da lì la shell ti dà tutto ciò che serve a un sito di lettura: initialChapterId e initialPanelId per riprendere dal punto in cui il lettore si era fermato, panelChange (con l’id della vignetta) e chapterChange per registrare i progressi e creare deep link, navigationAttempt per le schermate intermedie, error per la tua interfaccia di ripiego, e setVariable e changeLocale come metodi pubblici. Input come locale, autoplay e movimento ridotto si applicano in tempo reale mentre l’opera è aperta. initialVariables passa dati che l’host conosce già attraverso un canale privilegiato, accettato persino dalle variabili di sola lettura. I lettori possono impostare un segnalibro dalla barra degli strumenti, e l’opera si riapre da lì; la ripresa automatica («riprendi da dove avevi lasciato») è volutamente compito dell’host, e Saving Progress contiene un’implementazione completa con localStorage, pronta da incollare. I valori predefiniti definiti nel manifest, come visibilità dei balloon, tempi della riproduzione automatica, preset tipografici e strategia di precaricamento, sono elencati in Configuration.
Che cosa aggiunge la prossima release
La prossima release su npm è già nel ramo principale: è quella che i lettori vedono oggi nel reader PanelWave su read.panelwave.org e nell’anteprima del CMS. Le novità principali:
Ogni voce è documentata con la nota «from the release after 1.2.0» nella documentazione del player, così capisci a colpo d’occhio che cosa fa la versione che hai installato.
- Formato 1.7. I capitoli senza collegamenti nel grafo seguono semplicemente l’ordine delle vignette; le vignette non ancora sbloccate arrivano dal server come segnaposto bloccati; gli hotspot possono mostrare la propria etichetta come pulsante, così le opzioni di una scelta si vedono anche se l’illustrazione non le disegna; e il lettering dei balloon può avere qualsiasi colore.
- Una vista pagina che si adatta allo schermo. Il player sceglie l’impaginazione che l’autore ha preparato per quel dispositivo (il layout per smartphone su uno smartphone, quello per schermi grandi su un display 4K) e la cambia quando la finestra si ridimensiona. Il lettering si ridimensiona con la pagina e non scende mai sotto una dimensione leggibile.
- Una copertina e un inizio di lettura chiaro. Un’opera si apre sulla copertina e prosegue nella vista pagina; ogni tavola e ogni vignetta ha un proprio indirizzo condivisibile, e davanti a una scelta «avanti» chiede al lettore invece di decidere al posto suo.
- Schermo intero, animazioni e altro. Un pulsante per lo schermo intero, animazioni a keyframe dei livelli e movimenti di camera all’interno di una vignetta, più copertine alternative e una gestione più rigorosa del paywall, così una vignetta bloccata resta bloccata anche nella vista pagina e nella vista tela.
Per chi vuole contribuire
Se vuoi lavorare sulla libreria stessa, la documentazione ha un percorso per chi contribuisce: Architecture per la struttura del workspace, la gestione dello stato e il flusso dei dati dal manifest al rendering; Core Services per i servizi di manifest, stato, archivio delle variabili, motore di flusso, azioni degli hotspot, cache delle immagini, precaricamento, audio, video, tracciamento e diritti di accesso, e per come interagiscono; Components per shell, viewport, renderer dei livelli, overlay, finestre modali e barra degli strumenti; e Development per la configurazione, le suite Jasmine e Playwright, il linting e la build di produzione.
Cosa manca ancora
Preferiamo che tu lo legga qui piuttosto che scoprirlo in un log della console:
Ognuno di questi punti è registrato e documentato proprio dove lo incontreresti.
- Commenti e condivisione sono solo interfaccia. Il pannello dei commenti e la finestra di condivisione funzionano per il lettore, ma salvare i commenti è compito dell’host (Toolbar controls).
- La modalità manga e l’alto contrasto vengono salvati, ma non ancora applicati. La finestra delle impostazioni conserva la scelta del lettore, ma il layout non cambia di conseguenza.
- Nessuna ripresa automatica. Il player ripristina il segnalibro del lettore, non l’ultima vignetta letta; riprendere da dove il lettore si era fermato spetta all’host, con una ricetta pronta nella documentazione.
Provalo, integralo, mettilo alla prova
L’applicazione demo nel repository è l’integrazione di riferimento e può caricare qualsiasi manifest da un URL, quindi un’opera pubblicata dal CMS come link di anteprima si apre direttamente lì. Se costruisci qualcosa con il player, o trovi un manifest che il player mostra in modo sbagliato, l’issue tracker e la documentazione del formato sono i due punti da cui partire. Il formato è aperto, il player è MIT, e ciò che vedi nell’anteprima del CMS è esattamente questa libreria.

