A Frutiger Aero audio visualizer for Java Swing — playlist, transport, and nine Java2D presets — sitting on the Fruitiger Aero LAF glass shell rather than a flat spectrum bar.

Fruity Visualizer is a full Swing audio player/visualizer in the 2008 glass aesthetic. It installs Fruitiger Aero LAF early, opens an AeroFrame, and drives a Java2D scene from real decoded audio — sky, bubbles, glass ribbons, petals, tunnels — not a static EQ strip.

Spelling: the look is Frutiger; package/artifact names keep Fruitiger / Fruity, matching the sibling LAF.

Status: working end-to-end app (0.2.0-SNAPSHOT). MIT for this code; the runnable fat jar also ships LGPL 2.1 decoder jars (see NOTICE / LICENSES/). Java 17+.

What you get

Area Reality
Formats WAV (pure Java), MP3 (JLayer), FLAC (jFLAC), Ogg Vorbis (JOrbis) — magic-byte sniffed, seekable
Playback Java Sound SourceDataLine (+ NullPlayback for headless)
Analysis FFT → 64 log bins, beat / track profile; display spectrum in dB vs a following peak (ADR 0003)
UI shell AeroFrame, playlist rail, icon transport, scrub seek, level meter, theme toggle, fullscreen
Presets Nine layer stacks — Aqua Symphony through Warp Tunnel — smoke-verified
Graphics Four quality tiers + optional OpenGL accel (restart); settings in ~/.fruity-visualizer/
Packaging Self-contained -app fat jar (~1.4 MB) with LAF, decoders, fixtures, logo

Native decode (JniNativeDecoder) is an intentional inert seam — not required for the four formats above (ADR 0001).

Look

Idle shell on Fruitiger Aero LAF — same chrome, playlist rail, and transport; theme toggle flips Aqua Glass ↔ Midnight Aero without dropping the session.

Aqua Glass (idle)

Fruity Visualizer idle shell on Aqua Glass

Midnight Aero (idle)

Fruity Visualizer idle shell on Midnight Aero

Architecture

core  — pure Java: decode, playback, FFT / Analyzer (no Swing, no LAF, no Java2D)
ui    — Swing shell + Java2D scene + Fruitiger Aero LAF

The audio pump feeds the Analyzer; Swing reads the latest Metrics + spectrum via a volatile snapshot. Views (TopBar, TransportBar, PlaylistRail) stay passive; FruityVisualizer wires them to VisualizerEngine.

flowchart LR
  file[Audio file] --> registry[DecoderRegistry]
  registry --> pcm[PcmStream]
  pcm --> player[PlaybackBackend]
  pcm --> analyzer[Analyzer]
  analyzer --> metrics[Metrics + spectrum]
  metrics --> engine[VisualizerEngine]
  engine --> canvas[VisualizerCanvas]
  canvas --> layers[Scene layers]
  shell[FruityVisualizer shell] --> aeroLaf[Fruitiger Aero LAF]
  shell --> aeroFrame[AeroFrame]
  shell --> engine

Install happens once on the main thread before the tree is built (FruitigerAeroLookAndFeel.install(...)). Theme toggle re-installs Aqua Glass ↔ Midnight Aero live without killing the playlist.

Presets

Each preset is an ordered list of layers — no new renderer per preset:

Preset Key Idea
Aqua Symphony aquaSymphony Flagship: sky, bubbles, glass ribbon, petals, sparkles, vignette
Glass Ribbon glassRibbon Spectrum as one glass tube + sparkles
Bubble Field bubbleField Ambient droplets, low motion
Petal Bloom petalBloom Radial spectrum only
Alchemy alchemy Luminous filaments (WMP-inspired interpretation)
Trilogy I / II / III trilogyOne… Wings / parametric figures / layered terrain
Warp Tunnel warpTunnel Fly down a tube whose cross-section is the spectrum

Alchemy and Trilogy are original takes on classic WMP visualization behaviour, not ports of third-party code. Warp Tunnel is original to this project; tempo drives flight colour, beats raise obstacle density (TunnelLayerTest pins the behaviours from pixels).

Aqua Symphony

Flagship stack — bubbles, glass ribbon, radial petals — while a track is playing.

Aqua Symphony preset

Trilogy III

Layered terrain / wave planes driven by the spectrum.

Trilogy III preset

Warp Tunnel

Fly down a tube whose cross-section is the spectrum.

Warp Tunnel preset

Spectrum in decibels

Early builds looked bass-heavy because renderers clamped raw FFT magnitudes to [0,1] — most low bins pinned at 1.0 and “moved” while already flat. Analyzer.spectrumInto now maps each bin in dB relative to a slow-following spectral peak (−70…0 dB), the way a hardware analyser does. Absolute magnitudes stay available for band energies. Full case: docs/adr/0003-spectrum-display-scale.md.

Graphics quality

Tier Antialias Particles Paint scale
Best (default) on 100 % 100 %
Better on 85 % 100 %
Medium on 60 % 70 %
Low off 35 % 50 %

Hints alone aren’t enough — cost is dominated by full-canvas gradients and particle counts (ADR 0002 / 0004), so tiers also cut particle factors, bin strides, and internal resolution. Tiers apply next paint; hardware acceleration (sun.java2d.opengl) needs a JVM restart and is off by default. Gear button or S opens the dialog.

Acceptance bar: ≥ 55 fps at 100 % and 200 % UI scale (--bench / FrameRateBenchmarkTest). Raising UI scale shrinks the canvas inside a fixed shell size — the bench also reports a maximized scenario for honesty.

Run it

Needs the LAF in ~/.m2 first (sibling install or your Reposilite — see README). Then:

mvn install
mvn package
java -jar ui/target/fruity-visualizer-ui-0.2.0-SNAPSHOT-app.jar
java -jar ui/target/fruity-visualizer-ui-0.2.0-SNAPSHOT-app.jar track.flac
java -Dfruity.preset=warpTunnel -jar ui/target/fruity-visualizer-ui-0.2.0-SNAPSHOT-app.jar track.mp3
java -jar ui/target/fruity-visualizer-ui-0.2.0-SNAPSHOT-app.jar --smoke
java -jar ui/target/fruity-visualizer-ui-0.2.0-SNAPSHOT-app.jar --bench

Keys: Space play/pause · ←/→ ±5s · N/P next/prev · F fullscreen · O open · S settings.

Also understands LAF props (-Daero.theme=midnightAero, -Daero.uiScale=…) and Fruity ones (-Dfruity.preset=…, -Dfruity.quality=medium, --volume=35).

--smoke is the real pipeline check: logo pixels, decode all six bundled fixtures through DecoderRegistry, analyse, paint the loudest frame — not a “file exists” shrug.

License and attribution

App code is MIT. The runnable -app jar also ships third-party libraries that keep their own licenses (see NOTICE / LICENSES/ inside the jar). The MIT grant covers this project’s sources, not the LGPL decoders.

Library Maven Role License Links
Fruitiger Aero LAF com.fruitiger:fruitiger-aero-laf 0.1.0-SNAPSHOT Swing glass shell (AeroFrame, themes) MIT Project page
JLayer javazoom:jlayer 1.0.1 MP3 decode LGPL 2.1 Maven Central · SourceForge
jFLAC org.jflac:jflac-codec 1.5.2 FLAC decode LGPL 2.1 Homepage · Maven Central · GitHub (1.5.2 line)
JOrbis org.jcraft:jorbis 0.0.17 Ogg Vorbis decode (+ Ogg framer) LGPL 2.1 Homepage · Maven Central

WAV is pure-Java in-tree (no extra jar). The three LGPL decoders are unmaintained upstream and treated as vendored — fixture-tested via DecoderRegistry. Wider formats (M4A/AIFF/…) would be an additive ffmpeg or JNI decoder behind DecoderRegistry.registerFirst(...).

Where to look

Area Start here
Shell + CLI ui/.../ui/FruityVisualizer.java
Engine / playlist ui/.../engine/VisualizerEngine.java
Presets / layers ui/.../scene/Presets.java, ui/.../render/*
Decode registry core/.../audio/DecoderRegistry.java
Spectrum scale core/.../analysis/Analyzer.java, ADR 0003
Frame budget ADR 0002
Quality tiers ADR 0004
LAF dependency Fruitiger Aero LAF