Symfonium v14 is now out! My NPS style makes use of many of the new features. Give it a look if you want some ideas!
Now Playing Screen:


I’ve spent way too many hours on this
There is access to almost everything that Symfonium can do, as well as dynamic support for Classical music. (Some setup may be required.) No more swapping layouts when you turn on the Bach!
In Portrait, tap the Album Art to toggle “Mini Player” mode! (While in this mode, you’ll have to long-press the Album Artist name to go to their page.)
In Portrait’s “Full Player,” you can tap the bottom string to switch the displayed info.
NPS Details
There’s so many features packed into this (thanks Tolriq!) that I can’t include pictures for everything, but I’ll do my best to explain the highlights! (Or you can just try it out for yourself!)
Still here? Hope you don’t mind reading ![]()
The information displayed is very dynamic, but the positioning is kept mostly fixed. My goal was to show as much useful information as possible, without having the buttons jump around.
SPECIAL FEATURES
Expand
That’s right! Everyone loves some Special Features! This is where the fun comes in:
- Fully-Featured Queue Info!
- See pics above for demo
- Copy/paste version is included below!
- Mini-Player!
- Using the new “Toggle bool” feature, we can hide a bunch of strings with just a tap!
- Multi-line Strings taking too much room? Just toggle them off!
- Album Name
- For Singles - if Album Name matches Track Name, hides Album Name
- Avoids duplicate info
- Only hides if the track’s Release Type is set to “single”
- In other words, Michael Jackson’s “Thriller” album name will still display when playing the “Thriller” track
- For Singles - if Album Name matches Track Name, hides Album Name
- Disc Number and Disc Title
- Only shown when Disc Title is present
- Avoids useless “Disc 1” showing for your entire library
- Only shown when Disc Title is present
- Album Artist vs Performing Artists
- If they match, only displays one
- If they don’t match, you’ll see the Album Artist (bold and underlined) on its own line, with the Performing Artists filling the line after
- BUT
- If the Album Artist is listed among the Performing Artists, then you’ll see them all together
- This allows featured artists to be displayed without showing the Album Artist twice!
- Example 1: two Performing Artists - “Gawr Gura” and “Houshou Marine” have a collaboration unit name of “GuraMarine” (which is the Album Artist)
- You will see “GuraMarine” on the first line and “Gawr Gura, Houshou Marine” on the second line!
- Example 2: Album Artist “Hoshimachi Suisei” has a track on her album featuring additional artist “KAF”
- You will see “Hoshimachi Suisei, KAF” on a single line!
- Various Artists - Be gone!
- If the Album Artist is “Various Artists”, it will be hidden!
- You will only see the Performing Artists
- Cover songs
- If an Original Artist is set, checks to see if the Album Artist is the Original Artist
- If they don’t match, the artist line will show “Covered by: “ before the names!
- Alternately: if you set the track’s Release Type to “covers”, this header will be shown without needing an Original Artist
- Technical Stuff - Minimum Spacing
- Normally, if you set a string to “At Most 2” lines and it has no content to display, it will disappear (zero lines taken)
- I added cases which print Braille blanks (U+2800) to ensure that an “empty” string will collapse to a minimum of one line
- This allows e.g. String 2 to maintain the one-line spacing between the Title and Ratings when empty, while still being able to increase the line count if needed to fit the content!
- “But wait, there’s more!” This also allows me to still collapse the line completely when a bool is toggled!
Biggest Problems
Sizing
“I paid for the whole screen… and I’ma gon’ use the whole darned screen!” -Me, probably
Unfortunately this means that, while the layout fits perfectly on my screen… It might not fit so well on yours. In the post below this one, I’ll have some easy tweaks you can try to get it all to fit.
Light Mode
I hate Light Mode (in general) ![]()
I tried to make sure it is useable, and I think I did okay. But having to support Light Mode’s colors definitely held me back in a few ways (like making me disable the Compact Player’s shadow).
Handling Classical Music
The current version is set to display data for the tags “as-is” to make things easier for y’all to edit. (Meaning “Work Name” is expected in the “Work” field, rather than in the “Album” field. Which is where I would put that info.) Since there’s no consensus on how to tag Classical stuff, you’ll probably have to edit the strings to fit your own tags.
The difference here is that I’ve handled the “Classical” genre separately from the rest, so that you can potentially use one theme for all of your music.
More details in the “Custom Strings” post below!
Screenshots
In addition to the still shots from the animation above, the “Tag Demo” shows all the fields that can be displayed.
…plus fully-featured Queue Info!

This changes to reflect: repeat, shuffle, Smart Queue, Smart Flow, Sleep Timer, and end-of-queue… And it fits all of that in a normal screen-width!
Copy/Paste Ready
Feel free to use these in your own themes! If you want to know how they work, take a peek at the Exploded View section in the post below.
Split-Line Version
This is what you’re seeing in the demo above.
Top Line:
{⠀String by Celorien⠀%|!=%}{--------------------%|!=%}{# Headers%|!=%}{ %|!=%}{## Standard%|!=%}{𝄚𝄞𝄚⠀%|providername==empty || queue.position!=queue.size || player.repeat.mode=="One"%}{ %|!=%}{## End-of-Queue%|!=%}{𝄚𝄚𝄂⠀%|providername!=empty && queue.position==queue.size && player.repeat.mode!="One"%}{--------------------%|!=%}{# Sleep Timer%|!=%}{ %|!=%}{## Immediate Stop%|!=%}{⎅ %sleep.timer|sleep.timer.eos=="false" && sleep.timer.seconds>"0"%}{ %|!=%}{## Finish Track before Stop%|!=%}{⏲ %sleep.timer|sleep.timer.eos=="true" && sleep.timer.seconds>"0"%}{ %|!=%}{## Waiting to Stop%|!=%}{⚠⠀Ending playback⠀⚠%|sleep.timer.seconds=="0"%}{ %|!=%}{## Separator%|!=%}{ • %|sleep.timer.seconds>"0" && (providername!=empty && next.title!=empty)%}{--------------------%|!=%}{# Repeat All enabled, End-of-Queue reached%|!=%}{Restarting Queue%|(providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{ %|!=%}{## If set, show Queue Name; else ellipsis%|!=%}{: "%queue.name|(queue.name!=empty && queue.name!="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%"}{...%|(queue.name==empty || queue.name=="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{--------------------%|!=%}{# Queue Position%|!=%}{ %|!=%}{## Standard%|!=%}{Playing %queue.position|(smartqueue=="Disabled" && smartflow=="Disabled" && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{ / %queue.size|(smartqueue=="Disabled" && smartflow=="Disabled" && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{ %|!=%}{## Condensed - single-digit values padded to two digits%|!=%}{### Used when Smart Queue/Flow or Sleep Timer are enabled%|!=%}{▶ 0%queue.position|queue.position<"10" && ((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{▶ %queue.position|queue.position>"9" && ((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{--------------------%|!=%}{# Smart Queue and Flow Indicators%|!=%}{## Condensed indicators used when Sleep Timer is enabled%|!=%}{ • Smart %|((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{ %|!=%}{Queue%|(smartqueue!="Disabled" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}{[Q]%|(smartqueue!="Disabled" && sleep.timer.seconds>"0") && (providername!=empty && next.title!=empty)%}{ %|!=%}{ & %|((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}{ %|!=%}{Flow%|(smartflow!="Disabled" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}{[F]%|(smartflow!="Disabled" && sleep.timer.seconds>"0") && (providername!=empty && next.title!=empty)%}{--------------------%|!=%}
Bottom Line:
{⠀String by Celorien⠀%|!=%}{--------------------%|!=%}{# Headers%|!=%}{ %|!=%}{## Standard%|!=%}{Up Next:⠀%|(player.repeat.mode!="One" && player.shuffle.mode=="false") && (providername!=empty && next.title!=empty)%}{ %|!=%}{## Shuffle or Unknown Provider (e.g. Internet Radio)%|!=%}{⠀?????%|(player.repeat.mode!="One" && player.shuffle.mode=="true") && (providername!=empty && next.title!=empty)%}{⠀?????%|providername==empty%⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀}{ %|!=%}{## Repeat One%|!=%}{Repeat:⠀%|player.repeat.mode=="One" && (providername!=empty && next.title!=empty)%}{ %|!=%}{## End-of-Queue%|!=%}{⠀FIN%|queue.position==queue.size && (providername!=empty && next.title==empty)%}{--------------------%|!=%}{#!# EDIT BELOW to fit your tags%|!=%}{# Classical Genre%|!=%}{ %|!=%}{## Show Composer name (only if different from current)%|!=%}{%next.composer|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (next.composer!=empty && next.composer!=composer)%'s }{ %|!=%}{## Show Work name (only if different from current)%|!=%}{%next.work|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer!=composer) || ((next.composer!=empty && next.composer==composer) && next.work!=work))%}{ %|!=%}{## Show Movement Number as a Roman numeral (only if Movement name is set and Work matches current)%|!=%}{%next.movement.number.roman|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.number!=empty && next.movement.name!=empty)%. }{ %|!=%}{## Show Movement name (only if Work matches current%|!=%}{%next.movement.name|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer==composer) && next.work==work)%}{ %|!=%}{## Show Title (only if Work matches but Movement name is empty%|!=%}{%next.title|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.name==empty)%}{#!# END OF EDIT ZONE%|!=%}{--------------------%|!=%}{# Other Genres%|!=%}{ %|!=%}{## Show Work name if set, else show Title%|!=%}{%next.work|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work!=empty%}{%next.title|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work==empty%}{ %|!=%}{## Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}{ • %next.albumartist|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist!="Various Artists"%}{ • %next.artist|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist=="Various Artists"%}{--------------------%|!=%}
Single-Line Version
This version is used at the bottom of my Landscape layout. It’s not as pretty, but it shows the same info.
{⠀String by Celorien⠀%|!=%}{--------------------%|!=%}{⠀Mode and Position %|!=%}{--------------------%|!=%}{# Sleep Timer%|!=%}{ %|!=%}{## Immediate Stop%|!=%}{⎅ %sleep.timer|sleep.timer.eos=="false" && sleep.timer.seconds>"0"%}{ %|!=%}{## Finish Track before Stop%|!=%}{⏲ %sleep.timer|sleep.timer.eos=="true" && sleep.timer.seconds>"0"%}{ %|!=%}{## Waiting to Stop%|!=%}{⚠⠀Ending playback⠀⚠%|sleep.timer.seconds=="0"%}{ %|!=%}{## Separator%|!=%}{ • %|sleep.timer.seconds>"0" && (providername!=empty && next.title!=empty)%}{--------------------%|!=%}{# Repeat All enabled, End-of-Queue reached%|!=%}{Restarting Queue%|(providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{ %|!=%}{## If set, show Queue Name; else ellipsis%|!=%}{: "%queue.name|(queue.name!=empty && queue.name!="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%"}{...%|(queue.name==empty || queue.name=="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{--------------------%|!=%}{# Queue Position%|!=%}{ %|!=%}{## Condensed - single-digit values padded to two digits%|!=%}{### Used when Smart Queue/Flow or Sleep Timer are enabled%|!=%}{▶ 0%queue.position|sleep.timer.seconds!="0" && queue.position<"10" && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{▶ %queue.position|sleep.timer.seconds!="0" && queue.position>"9" && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{--------------------%|!=%}{# Smart Queue and Flow Indicators%|!=%}{## Always uses condensed indicators%|!=%}{ • %|((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{[Q]%|(smartqueue!="Disabled" && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{[F]%|(smartflow!="Disabled" && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{--------------------%|!=%}{⠀Next Track Info %|!=%}{--------------------%|!=%}{# Headers%|!=%}{ %|!=%}{## Standard%|!=%}{ • Up Next:%|sleep.timer.seconds!="0" && player.repeat.mode!="One" && (providername!=empty && next.title!=empty)%}{ %|!=%}{## Shuffle%|!=%}{⠀?????%|sleep.timer.seconds!="0" && ((player.repeat.mode!="One" && player.shuffle.mode=="true") && providername!=empty && next.title!=empty)%}{ %|!=%}{## Repeat One%|!=%}{ • Repeat:%|(sleep.timer.seconds!="0" && player.repeat.mode=="One" && providername!=empty && next.title!=empty)%}{ %|!=%}{## End-of-Queue%|!=%}{### Bullet Spacer shows when Sleep Timer active%|!=%}{### Split string "\t" keeps Header position fixed when Marquee enabled%|!=%}{ • %|queue.position==queue.size && (providername!=empty && next.title==empty) && sleep.timer.seconds>"0"%}{FIN%|queue.position==queue.size && (providername!=empty && next.title==empty) && sleep.timer.seconds!="0"%}\t{### Blanks extend tap region%|!=%}{⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀%|queue.position==queue.size && (providername!=empty && next.title==empty) && sleep.timer.seconds!="0"%}{--------------------%|!=%}{#!# EDIT BELOW to fit your tags%|!=%}{# Classical Genre%|!=%}{ %|!=%}{## Show Composer name (only if different from current)%|!=%}{%next.composer|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (next.composer!=empty && next.composer!=composer)%'s }{ %|!=%}{## Show Work name (only if different from current)%|!=%}{%next.work|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer!=composer) || ((next.composer!=empty && next.composer==composer) && next.work!=work))%}{ %|!=%}{## Show Movement Number as a Roman numeral (only if Movement name is set and Work matches current)%|!=%}{%next.movement.number.roman|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.number!=empty && next.movement.name!=empty)%. }{ %|!=%}{## Show Movement name (only if Work matches current%|!=%}{%next.movement.name|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer==composer) && next.work==work)%}{ %|!=%}{## Show Title (only if Work matches but Movement name is empty%|!=%}{%next.title|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.name==empty)%}{#!# END OF EDIT ZONE%|!=%}{--------------------%|!=%}{# Other Genres%|!=%}{ %|!=%}{## Show Work name if set, else show Title%|!=%}{%next.work|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work!=empty%}{%next.title|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work==empty%}{ %|!=%}{## Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}{ • %next.albumartist|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist!="Various Artists"%}{ • %next.artist|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist=="Various Artists"%}{--------------------%|!=%}
App Style
App Style Details
I know the theme is called “AMO-RED,” but all the red parts will actually change color once you start playing music!
Suggested Music - Uses two Smart Playlists to help you pick something to listen to. Check the “Playlist Setup” section below to get this set up.
Dark is the new Black - Make sure not to “Black” mode, as it overrides some of my colors. “Dark” mode has fully black backgrounds in this theme anyway.
Designed as a 2-3 page layout - I left some gaps to hide my phone’s camera cutout, but you’ll probably need to adjust the spacing a bit to suit your phone. Fortunately, the app now makes that pretty easy to do!
Minimal design - I only use the Home Page as a landing screen, so I didn’t want too much on it. There are quick shortcuts to most media options, but the Library tab is where I do my browsing.
Newly-added music page will pop in at the top if you add some new music. If you use my Playlist settings below, the page will also go away once you’ve listened to the new stuff.
Scrollable previews:
Expand
No, the “Recently Added” section isn’t cut off. It scrolls horizontally. (These pictures don’t, of course.)
Media Overviews:
Here we have: Artist, Album, Playlist, and Genre views! The durations on the Artist and Album pages use my localized strings to show a prettier estimate instead of the exact duration.
Playlist Setup:
For both Recents and Suggested sections
To get you started, I enabled the default “Recently Added” categories. I strongly recommend replacing them with your own Smart Playlists, using the following settings:
Playlist Settings
Same info, however you want to see it.
“Max Item count” can be whatever you prefer.
Text
- Recently Added Albums
- (Date Added: within 14)
- AND
- (Play Count: less than 1)
- AND
- (Track count: greater than 1).
- Sort by “Date added” with the arrow pointing UP
- Inherit providers
- (Date Added: within 14)
- Recently Added Tracks:
- (Date Added: within 14)
- AND
- (Play Count: is 0)
- Sort by “Date added” with the arrow pointing UP
- Inherit providers
- (Date Added: within 14)
- Suggested Albums:
- GROUP: (Last Played: is missing) OR (Last Played: over 30)
- AND
- (Track Count: greater than 1)
- Sort by “Random”
- Inherit providers
- GROUP: (Last Played: is missing) OR (Last Played: over 30)
- Suggested Tracks:
- (Last Played: is missing)
- OR
- (Last Played: over 30)
- Sort by “Random”
- Inherit providers
- (Last Played: is missing)
Once you have your playlists added to the Home page, you can just copy the layout from the existing Recents rows. (There’s new buttons at the bottom, when editing the Home page rows.) Easy!
For the Suggested Albums/Tracks, you can instead use the new “Change Playlist” button to replace the empty section with your newly-created playlist.
This setup for Recents let you customize how many days new songs will stay in the list, but more importantly it will remove a song from the Recents list once you have listened to it. (Get that list cleared!
)
Downloads
Expand
Final version is now available! Full-size version available here:
NPS_FULL COMBO v1.0P.json (63.5 KB)
NavBar version is scaled down for those that have shorter screens than me. Hopefully it’s not too tiny.
NPS_FULL COMBO (NavBar) v1.0P.json (63.5 KB)
App Style: APPSTYLE_AMO-RED v1.0P.json (123.1 KB)
Color theme only: COLORS_AMO-RED.json (2.7 KB)
Careful to import the right file, since I failed to come up with a separate name for App Style vs Color Theme ![]()
Version Archive
Expand
Posted: 2026-02-24 at 05:00 UTC
Standalone NPS: nps-Full Combo v0.10 (14B6) (67.1 KB)
Mostly just small tweaks to the strings. Added a STOP button to landscape’s Bottom Bar (replaces Details button)
Posted: 2026-02-20 at 01:00 UTC
Standalone NPS: nps-Full Combo v0.9 (14B5)-20260220_171047-symfonium.json (67.1 KB)
Huge makeover for Portrait! Screenshots will be updated in the coming days…
Posted 2026-02-13 at 05:00 UTC
Standalone NPS: nps-Full Combo v0.5 (14B1) (61.4 KB)
Added Mini Player mode! (Toggle by long-pressing Ratings)
Misc. bug fixes
Posted 2026-02-12 at 05:20 UTC
Full App Style: as-AMO-RED v0.3 (14B1) (113.0 KB)
Standalone NPS: nps-Full Combo v0.3 (14B1) (58.1 KB)
Color theme only: AMO-RED (2.7 KB)
Image credits:
Screenshot frames sourced from MockUPhone.com
Text-art created by CoolText.com

































