Dopamine 3, an Angular player in an Electron shell, and the files it owns
The audio player that keeps it simple
At a glance
- What is it?
- Dopamine is a desktop music player for Windows, Linux and macOS, rewritten in Electron, Angular and TypeScript, with a file-based translation system that copies its bundled translations into your application data directory and then never overwrites your edits. The build is constrained by one native dependency, the distribution ships through four package channels with three different names, and postinstall patches two libraries in place.
- Who is it for?
- Use Dopamine if you want a local library player that keeps its data in files you can read and edit, and be prepared to build from source on the same operating system as your target package, because better-sqlite3 rules out cross-building. Do not install the AUR package named dopamine, which the author states they do not maintain, and read the postinstall scripts before you run npm install in an environment where modifying node_modules is a concern.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Dopamine 3 is, and where its code actually runs
Dopamine is a desktop audio player whose whole pitch is restraint. The README calls it an audio player that tries to make organising and listening to music as simple and pretty as possible, and the current version is written with Electron, Angular and TypeScript and works on Windows, Linux and Mac.
That combination has a consequence the README is unusually frank about, in the debugging section. Most of the code runs in the Electron renderer, which is why the project provides a renderer debugging configuration and, as it puts it, only a renderer configuration for now. An Angular application in a renderer is a web application with a desktop shell around it, and it means the UI framework, the layout system and the state management are all the web versions of those things.
The payoff is a player whose interface is built with modern front-end tooling rather than a native widget set, which is why the visual language in the screenshots looks closer to a web dashboard than to a 2010s desktop player. The cost is the one the build documentation makes unavoidable: the application is an Electron app with a native database dependency, so the packaging story is the packaging story of any Electron application that also ships a compiled SQLite binding.
The project itself is long-lived. The copyright line in the manifest reads Digimezzo, 2014 to 2026, and the current line is version 3.0.x with weekly patch releases. The manifest is marked private, so nothing here is published to npm; Dopamine is distributed as an application, and the package manifest is a build input rather than a distribution channel.
better-sqlite3 is the reason you cannot build one package for three systems
The build instructions contain one sentence that determines the shape of any contribution to this project, and it is a native module constraint:
git clone https://github.com/digimezzo/dopamine.git
cd dopamine
npm install # Install dependencies
npm start # Start Dopamine
npm run electron:linux # Build for LinuxThe sentence before the commands says that because of the native dependency better-sqlite3, the project cannot be built for all platforms on GNU/Linux. The GNU/Linux packages must be built on GNU/Linux, the Windows package on Windows, and the macOS package on macOS. There is no cross-compilation path and no workaround described.
That has three practical effects. A contributor who wants to produce a Linux package needs a Linux machine, which is why the prerequisites are documented separately for Ubuntu, Manjaro, Windows and macOS rather than once. A packager cannot build all three artefacts in one CI job without a matrix of three runners. And a bug that only reproduces on one operating system cannot be bisected on another.
The reason a music player needs better-sqlite3 at all is that the library, its tags and its play history are state, and state belongs in a database rather than in a directory of JSON files that has to be rewritten on every change. The trade is a fast, queryable store in exchange for a build that must happen on the target system.
The test layout shows the project takes that seriously. Alongside the ordinary `jest.config.js` there is a `jest.database.integration.config.js`, a separate configuration dedicated to database integration testing, plus `jest.setup.js` and an `electron-jest.js` shim. Splitting the database tests into their own project is the right call when the storage layer is a native binding, because those tests fail for reasons the unit tests cannot see.
The Translations directory, and why it never overwrites your work
The most carefully specified feature in this README is not a feature most projects document at all: translations.
On first run, Dopamine creates a `Translations` directory inside its application data directory, next to `Dopamine.db`, and copies all of its bundled translations into it. From that point the files are yours. You can copy one of the JSON files to a new language code, such as `xx.json`, or edit an existing one.
The rules around updates are what make the design sound. Dopamine adds missing bundled files again when the language menu opens, so a new release that introduces a language shows up, and it never overwrites your edits, so a corrected string survives every upgrade. A user file with the same name as a bundled language overrides that language's translations entirely, which is a blunt but predictable rule. Missing keys fall back to the default English translation, unless the file is itself an override of English.
The format is a flat key and value mapping, and a new language needs two specific keys so it can be labelled in the welcome and appearance language menus:
{
"language-name-english": "Example language",
"language-name-localized": "Example name",
"welcome-to-dopamine": "Welcome!"
}There is one workflow detail that will save you an hour if you know it in advance. To test an edit to a translation that is already selected, you have to select another language and then switch back, because the file is reloaded on the language change rather than watched. The menu also discovers new files when it is opened, so a language file you dropped in while the application was running is picked up without a restart.
Compare this with the usual approach, where translations live inside the application or in a directory the application owns, and updating means either losing local changes or maintaining a patch. Dopamine pushes the files into your data directory specifically so that the user, not the application, holds the authoritative copy. The one cost is that the copy step is invisible until you go looking for it.
The development loop: a dev server on 4200 and a debug port on 9222
The two script definitions in the manifest explain the whole development setup, and they interlock.
Starting the application is a parallel run of the Angular dev server and the Electron serve step, expressed as `npm-run-all -p ng:serve electron:serve`. The Electron step is the interesting one, and it reads as a chain of three conditions: wait for the dev server, compile the Electron-side TypeScript, then launch Electron against it.
"postinstall": "electron-builder install-app-deps && node patch-discord-rpc.js && node patch-taglib-sharp.js",
"electron:serve": "wait-on http-get://localhost:4200/ && npm run electron:serve-tsc && electron . --serve --remote-debugging-port=9222",The wait uses `wait-on` against an HTTP GET on port 4200, which is the Angular dev server's default, so the shell does not launch against a port nothing is listening on. The compile step is a separate TypeScript project, `tsc -p tsconfig-serve.json`, which is why the repository carries four separate tsconfig files: a base, an app configuration for the Angular build, a spec configuration for tests, and the serve configuration for the Electron main process. An Electron application has two compilation targets, and pretending otherwise is the usual source of type errors that only appear at runtime.
The final flag is what makes the documented debugging workflow possible. Electron is launched with `--remote-debugging-port=9222`, and the README says the `.run` folder contains a debugging configuration called Debug renderer that attaches to the Dopamine instance started by `npm start`. The recommended IDEs are JetBrains Rider and WebStorm, both of which read that folder natively. Since the README states that most of the code runs in the renderer, that is why there is a renderer configuration and no main-process one, and why the remote debugging port rather than a Node inspector flag is the mechanism.
For a project of this size, having the debug configuration checked in rather than described in a wiki is the difference between a new contributor being productive in an hour and in a day.
postinstall patches two libraries, and the AppImage is rebuilt for fuse3
Two lines in the manifest are worth reading before anyone runs `npm install` in a controlled environment.
The first is the postinstall script, which is three commands: `electron-builder install-app-deps`, then `node patch-discord-rpc.js`, then `node patch-taglib-sharp.js`. The first of those is standard, rebuilding native dependencies for the Electron ABI. The other two are project-specific patches applied to installed packages, and the names say what they target: a Discord rich presence library and the taglib and sharp native bindings.
Those two are exactly the kind of dependency that needs patching in an Electron application. A Discord RPC library that links against a system or prebuilt native module will not match Electron's ABI until it is rebuilt, and the tag reading and image processing stack that a music player needs has the same problem. Fixing it in a postinstall step rather than pinning a fork is a pragmatic choice, and it has an obvious consequence: running the installer modifies files inside `node_modules`. That is worth knowing before you install in a build pipeline with an integrity check on your dependency tree, and it is also the kind of thing that breaks silently when the upstream library changes shape, since the patch script has to keep matching.
The second detail is in the Linux build script, which does three things: build the production bundle, run electron-builder with an explicit config file, and then run `scripts/repackage-appimage-fuse3.sh` with the arguments `release x86_64`. That last step is a repair, not a packaging nicety. AppImages traditionally needed fuse2 to mount themselves, and a distribution that removed fuse2 from its repositories breaks every AppImage that still depends on it, so repackaging to bundle fuse3 is the standard workaround. Doing it in a build script means the resulting AppImage works on current distributions without the user installing anything.
The pattern across both of these is worth naming: this project solves packaging problems with scripts inside the repository rather than with instructions in the documentation. That is more maintainable, and it is also why the top level has a `scripts/` directory, a `build/` directory and a `deployment/` directory.
Four install channels, three names, and two dependency traps
Dopamine is distributed through four channels, and each has a detail that will cost you time if you do not know it beforehand.
The Snap Store is the easiest path, with `snap install dopamine` for the store version. A manually downloaded snap is a different operation, because a locally downloaded snap is unsigned as far as the store is concerned, so it needs `snap install --dangerous Dopamine-x.y.z.snap`. That flag bypasses the store's confinement checks, which is a deliberate and visible risk on the part of whoever runs it.
The Arch User Repository is where the naming trap is. Dopamine is available as `dopamine-official`, installed with `yay -S dopamine-official`, and the README adds a note that the AUR package named `dopamine` is not maintained by the author. Two packages, nearly identical names, one of them someone else's. Anyone installing by search rather than by the documented name gets the wrong one.
The native packages are installed directly. A pacman package goes in with `sudo pacman -U Dopamine-x.y.z.pacman`, and if that fails on a missing `libappindicator-sharp` dependency there is a documented fallback that assumes the package is present:
sudo pacman -U Dopamine-x.y.z.pacman --assume-installed libappindicator-sharp
yay -S dopamine-officialAn rpm package installs with `sudo dnf install Dopamine-x.y.z.rpm`. Both require you to have downloaded the file and substituted the version, which is the normal shape for a project without a distribution channel of its own.
There is also a Snap-specific limitation the README starts to explain and the excerpt cuts off. The `removable-media` plug was added to the Snap configuration to allow access to `/media`, and the text notes that unlike the `home` plug it does not behave the same way. On a system where your music lives on a mounted removable volume rather than in the home directory, that difference is the difference between the library appearing and not appearing, so it is the first thing to check if Dopamine starts with an empty library under Snap confinement.
The licence is the GNU General Public License version 3, declared in the manifest with a copyright line covering 2014 to 2026, and the text in the `LICENSE` file. The full text of the GPL is what governs distribution and modification of the application; the individual codecs and libraries it links are separate questions.
Dopamine against a browser player, and the case for a local library at all
The obvious alternative is a browser-based player or a streaming service client, and the difference in approach is not features, it is where the audio and the metadata live.
A streaming client holds your library on someone else's servers. You get a catalogue you did not have to assemble, synchronisation across devices for free, and recommendations. What you do not get is the folder. Your tags, your file names, your folder structure, your album art on disk and your play counts are the service's data, and if you leave, they stay. Some services also compress or re-encode what they serve, which is a decision made for bandwidth rather than for your ears.
Dopamine's approach is the opposite. It reads a local library, and the evidence for how seriously it takes that is better-sqlite3 plus a dedicated database integration test configuration. Tags come from your files, art comes from your files or is embedded, and the database is a cache and an index of something you already own. The Snap's `removable-media` plug exists precisely so that a library on a mounted drive counts as local, which is a small detail that tells you how library-location-sensitive the design is.
The third option is a native player from a different lineage, either another desktop application or a small dedicated one. That comparison is less about architecture than about interface, and here Dopamine's Electron and Angular base is an advantage for anyone who wants to modify it: the debugging workflow is a browser debugger, the translations are JSON files in your data directory, and the test suite is Jest.
The case where Dopamine is the wrong tool is the streaming case. If your music is on a service rather than on disk, none of this applies, and the Snap confinement restrictions around filesystem access are a tax you pay for a confinement model you may not want. The other wrong case is a very large library on a slow disk, where the index build is a one-time cost the README does not discuss.
Weekly patches in a 3.0.x line, and a leftover document in the repository root
The release pattern says this is an actively maintained application rather than an abandoned one. Version 3.0.9 was published on 2026-08-19, 3.0.10 on 2026-08-28, and 3.0.11 on 2026-09-26, with the last push to the repository on 2026-09-27. Patch releases roughly weekly inside a single minor line, which is the cadence of a project with a user base that reports problems and a maintainer who ships fixes rather than accumulating them.
The presence of a `CHANGELOG.md` at the top level is what makes that cadence usable, and there is a workflow badge for nightly builds in the README, so there is at least a build of unreleased code available to people who want it. The manifest version, 3.0.11, matches the newest tag, so the version numbers are consistent here, which is more than several projects in this series manage.
Two things in the top-level listing are worth mentioning because they show how the repository has grown. There is a `RATING_BACKUP_IMPLEMENTATION.md` sitting in the root, which is a design document about a specific feature rather than a repository-level document, and it is the only markdown file in the root besides the README and the changelog. Design notes for individual features have a way of accumulating at the top level in applications that predate a docs directory, and it is a small sign of where the project's tidiness is heading.
The other is the spread of tooling configuration, which is extensive and mostly conventional: `.eslintrc.json`, `.prettierrc`, `.browserslistrc`, `.npmrc`, `.editorconfig` is absent but `.prettierrc` is present, `angular.json`, `angular.webpack.js`, `postcss.config.js`, four tsconfig files, a `jest.setup.js`, an `electron-jest.js` shim, a `_config.yml` for the GitHub Pages site, and both `.run/` and `.vscode/` directories for the two IDE families the project supports. Having both is deliberate rather than accidental, and it follows from a README that recommends JetBrains tooling while the project also ships a Visual Studio configuration.
For a user, the practical takeaway is that Dopamine 3 is a moving but fast-moving target, that the packaging is unusually well served for an independent desktop project, and that the parts of it most likely to surprise you, the Snap confinement and the AUR naming, are both documented in the README if you read past the screenshots.
Editorial conclusion
Use Dopamine if you want a local library player that keeps its data in files you can read and edit, and be prepared to build from source on the same operating system as your target package, because better-sqlite3 rules out cross-building. Do not install the AUR package named dopamine, which the author states they do not maintain, and read the postinstall scripts before you run npm install in an environment where modifying node_modules is a concern. Verify first by opening the Translations directory next to Dopamine.db in your application data directory, editing a key, and switching language twice to confirm the reload behaviour, then check CHANGELOG.md for what changed in the 3.0.x line you are about to install.
Frequently asked questions
What is Dopamine and what is it built with?
Dopamine is a desktop audio player that aims to make organising and listening to music simple, written with Electron, Angular and TypeScript and working on Windows, Linux and Mac. The README notes that most of the code runs in the Electron renderer, which is why only a renderer debugging configuration is provided.
Why does Dopamine have to be built on the target operating system?
Because of the native dependency better-sqlite3. The README states the project cannot be built for all platforms on GNU/Linux, so the GNU/Linux packages must be built on GNU/Linux, the Windows package on Windows and the macOS package on macOS. Node.js 22 and the packaging tools for your target are the prerequisites.
How do I add a new language to Dopamine?
Dopamine creates a Translations directory inside its application data directory next to Dopamine.db and copies the bundled translations there. Copy one of those JSON files to a new language code, keep the flat key and value format, and include language-name-english and language-name-localized so the language can be labelled in the menus.
Will a Dopamine update overwrite my translation edits?
No. Dopamine adds missing bundled files again when the language menu opens but never overwrites your edits, and a user file with the same name as a bundled language overrides that language's translations. Missing keys fall back to the default English translation unless the file itself overrides English.
How do I install Dopamine on Linux?
Through the Snap Store with snap install dopamine, through the AUR as dopamine-official with yay -S dopamine-official, or from a downloaded pacman or rpm package. The README notes that the AUR package named dopamine is not maintained by the author, and a manually downloaded snap requires snap install --dangerous.
How do I debug Dopamine, and what does npm install do to dependencies?
The README recommends JetBrains Rider or WebStorm and provides a Debug renderer configuration in the .run folder that attaches to the instance started by npm start, with most code in the renderer. Note that the postinstall script runs electron-builder install-app-deps and then patches two installed libraries, patch-discord-rpc.js and patch-taglib-sharp.js.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/digimezzo-dopamine)