Installation
Install @panelwave/player from npm into an Angular application — supported Angular and ngx-translate versions, peer dependencies, required providers, UI translation files, and balloon fonts.
The player is published on npm as @panelwave/player (MIT). This page covers getting it into your application and the one-time app setup it needs.
Requirements
- An Angular 20, 21 or 22 application (standalone components or NgModules both work; zone.js and zoneless change detection both work)
@angular/common/httpavailable — the player loads manifests and its UI translation files over HTTP- A browser from the supported set: Chrome / Edge 120+, Firefox 120+, Safari 17+ (desktop and iOS), Android Chrome 120+
Peer dependencies
The package declares:
{
"peerDependencies": {
"@angular/common": "^20.0.0 || ^21.0.0 || ^22.0.0",
"@angular/core": "^20.0.0 || ^21.0.0 || ^22.0.0",
"@ngx-translate/core": "^17.0.0 || ^18.0.0",
"rxjs": "^7.8.0"
},
"dependencies": {
"json-logic-js": "^2.0.5",
"tslib": "^2.3.0"
}
}
json-logic-js (condition evaluation) is installed automatically; the Angular packages, RxJS and ngx-translate come from your app.
The Angular 21 / 22 and ngx-translate 18 ranges are new in player 1.2.0, released on npm on 2026-09-29. Player 1.1.0 and earlier declare ^20.0.0 and ^17.0.0, so npm refuses a strict-peer install of those on a newer app — install @panelwave/player@latest. The package is compiled with Angular 20 in partial-compilation mode and linked by your app's Angular version; every supported major is verified in CI by installing the packed library into a fresh app, building it for production and rendering a sample work.
Install
npm install @panelwave/player @ngx-translate/core
Releases follow semver; the changes in each version are listed in the repository's CHANGELOG.md and on the GitHub releases page. Every release is published from GitHub Actions with npm provenance, so npm shows which workflow run built the tarball you install.
Need an unreleased fix? Build the library from source instead (npm install, then ng build player --configuration production) and install dist/player with npm install <path>/dist/player. See Development & Contributing.
Application setup
The player ships as standalone components — there is no NgModule to import. You import PlayerShellComponent directly into the imports array of your own standalone component (or into an NgModule's imports if you still use modules).
1. Providers
Two application-level providers are required:
provideHttpClient()— used byManifestService(formanifestUrl) and the translation loader.provideTranslateService(...)— the player's UI chrome (toolbar labels, dialogs, paywall text, loading/error text) is translated with@ngx-translate/core. The host application configures the loader.
// app.config.ts
import { ApplicationConfig, inject } from '@angular/core';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import { TranslateLoader, TranslationObject, provideTranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
/** Loads the player UI translations from your app's assets. */
class PlayerTranslateLoader implements TranslateLoader {
private readonly http = inject(HttpClient);
getTranslation(lang: string): Observable<TranslationObject> {
return this.http.get<TranslationObject>(`./assets/i18n/${lang}.json`);
}
}
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
provideTranslateService({
loader: { provide: TranslateLoader, useClass: PlayerTranslateLoader },
}),
],
};
This form works with ngx-translate 17 and 18. TranslateModule.forRoot() still works on 17 but no longer exists in 18.
Pass the loader as an explicit { provide: TranslateLoader, useClass: ... } provider, as shown. On ngx-translate 18, provideTranslateLoader(PlayerTranslateLoader) fails in production builds for a plain (non-@Injectable) class: 18 tells classes from factory functions by their source text, and the minified class no longer looks like one ("Class constructor … cannot be invoked without 'new'").
The player depends on json-logic-js, a CommonJS module. To silence the build warning about it, add "allowedCommonJsDependencies": ["json-logic-js"] to your build options in angular.json.
2. UI translation files
The package ships its UI strings in English and German under src/assets/i18n/ (en.json, de.json). Copy them into your build output so the loader above finds them at assets/i18n/<lang>.json:
// angular.json → projects.<app>.architect.build.options
"assets": [
{ "glob": "*.json", "input": "node_modules/@panelwave/player/src/assets/i18n", "output": "assets/i18n" }
]
The player maps content locales like de-DE to the base language de when picking the UI language — see Localization. If your app has its own ngx-translate files, merge the player's keys into them instead of copying.
Serve the files from the package, not from an older copy. Labels added in newer releases (paywall, like/bookmark, branch chooser, …) show up as raw keys such as toolbar.like when the JSON files are out of date.
3. Balloon fonts
Speech balloons render with real comic-lettering fonts. The open-licensed set (SIL OFL 1.1 — Bangers, Comic Neue, Caveat, Anton, and ten more) ships with the package under src/assets/fonts/balloon/. Include the @font-face declarations once in your application:
// angular.json → projects.<app>.architect.build.options
"styles": [
"node_modules/@panelwave/player/src/assets/fonts/balloon/balloon-fonts.css",
"src/styles.css"
]
Alternatively, @import the file from your global stylesheet — the font URLs are relative to the CSS file, so both approaches work.
Ames Pro (Blambot) is a commercial font and is not included in this MIT package, nor in works exported from the PanelWave CMS. The default balloon font stack is 'Ames Italic', 'Comic Neue', sans-serif — without a license for Ames, balloons fall back to Comic Neue. If your works use Ames, license it from blambot.com and add your own @font-face declarations for 'Ames Italic', 'Ames Bold Italic', and 'Ames Regular'. See Fonts and Ames Pro.
The speech-bubble overlay re-renders once document.fonts.ready resolves, so balloons are measured with the real fonts even when they load late.
UI font
From the player release after 1.2.0, the player chrome (toolbar, thumbnail strip, dialogs, form controls) uses the PanelWave UI font Barlow, falling back to the system UI font. The package does not ship Barlow: load it in your app (it is open-licensed, SIL OFL) or set your own font for the player with the --pw-font-ui custom property:
pw-player-shell {
--pw-font-ui: 'Inter', system-ui, sans-serif;
}
Verify the setup
Import the component and point it at a manifest — if the providers are missing you will see injection errors for HttpClient or TranslateService at startup:
import { Component } from '@angular/core';
import { PlayerShellComponent } from '@panelwave/player';
@Component({
selector: 'app-reader',
imports: [PlayerShellComponent],
template: `<pw-player-shell manifestUrl="/stories/my-story/panelwave.json" [showToolbar]="true" />`,
styles: `:host { display: block; height: 100dvh; }`,
})
export class ReaderComponent {}
If the toolbar shows keys like toolbar.settings instead of labels, the translation files are not being served — check the assets entry above.
Next: Quickstart: Embed the Player walks through loading a manifest and handling player events.