Create a Cross-Platform Java Audio Player with JavaFX
This guide shows how to build a simple, cross-platform audio player in Java using JavaFX. It covers project setup, playing common audio formats, a minimal UI with playback controls, and packaging notes so your app runs on Windows, macOS, and Linux.
What you’ll build
A desktop audio player that can:
- Load and play audio files (MP3, WAV, AAC where supported)
- Play / pause, stop, seek, and adjust volume
- Display current time and duration
Requirements
- Java 11+ (OpenJDK or Oracle JDK)
- JavaFX SDK matching your JDK (if using modular Java) or OpenJFX on the module path
- Maven or Gradle (examples use Maven)
- Optional: mp3 support via third-party decoders (JavaFX supports MP3 out of the box in most builds; if not, use vlcj or JLayer)
Project setup (Maven)
Add JavaFX dependencies to pom.xml (example for JavaFX 20 — match to your version and platform). Use the OpenJFX artifacts and include javafx-controls and javafx-media.
Example dependencies snippet (replace version and classifier for your OS when launching):
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>20</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>20</version>
</dependency>
</dependencies>
When running, specify JavaFX modules on the module path or use the javafx-maven-plugin to handle runtime modules.
Key classes and APIs
- javafx.scene.media.Media — represents a media resource (file URL or remote URL)
- javafx.scene.media.MediaPlayer — controls playback (play, pause, stop, seek)
- javafx.scene.media.MediaView — for video; not needed for audio but usable for visualization hooks
- javafx.scene.control.* — Buttons, Sliders, Labels for UI
Minimal working example
Below is a concise JavaFX application demonstrating the core player functions. It uses a file chooser to load local audio files and provides playback controls, a seek slider, and a volume slider.
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import java.io.File;
public class SimpleAudioPlayer extends Application {
private MediaPlayer mediaPlayer;
private Slider seekSlider = new Slider();
private Label timeLabel = new Label(“00:00 / 00:00”);
private Slider volumeSlider = new Slider(0, 1, 0.5);
@Override
public void start(Stage primaryStage) {
Button openBtn = new Button(“Open”);
Button playBtn = new Button(“Play”);
Button pauseBtn = new Button(“Pause”);
Button stopBtn = new Button(“Stop”);
openBtn.setOnAction(e -> {
FileChooser fc = new FileChooser();
fc.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter(“Audio Files”, “.mp3”, “.wav”, “.aac”, ”*.m4a”)
);
File file = fc.showOpenDialog(primaryStage);
if (file != null) loadMedia(file);
});
playBtn.setOnAction(e -> { if (mediaPlayer != null) mediaPlayer.play(); });
pauseBtn.setOnAction(e -> { if (mediaPlayer != null) mediaPlayer.pause(); });
stopBtn.setOnAction(e -> { if (mediaPlayer != null) mediaPlayer.stop(); });
seekSlider.setPrefWidth(400);
volumeSlider.setPrefWidth(120);
volumeSlider.valueProperty().addListener(o -> {
if (mediaPlayer != null) mediaPlayer.setVolume(volumeSlider.getValue());
});
HBox controls = new HBox(8, openBtn, playBtn, pauseBtn, stopBtn, new Label(“Volume”), volumeSlider);
controls.setPadding(new Insets(10));
BorderPane root = new BorderPane();
root.setTop(controls);
HBox bottom = new HBox(10, seekSlider, timeLabel);
bottom.setPadding(new Insets(10));
root.setBottom(bottom);
Scene scene = new Scene(root, 640, 140);
primaryStage.setScene(scene);
primaryStage.setTitle(“Simple JavaFX Audio Player”);
primaryStage.show();
}
private void loadMedia(File file) {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.dispose();
}
Media media = new Media(file.toURI().toString());
mediaPlayer = new MediaPlayer(media);
mediaPlayer.setVolume(volumeSlider.getValue());
mediaPlayer.setOnReady(() -> {
Duration total = media.getDuration();
seekSlider.setMax(total.toSeconds());
updateTimeLabel();
});
mediaPlayer.currentTimeProperty().addListener((obs, oldTime, newTime) -> {
if (!seekSlider.isValueChanging()) {
seekSlider.setValue(newTime.toSeconds());
updateTimeLabel();
}
});
seekSlider.valueChangingProperty().addListener((obs, wasChanging, isChanging) -> {
if (!isChanging) mediaPlayer.seek(Duration.seconds(seekSlider.getValue()));
});
seekSlider.valueProperty().addListener((obs) -> {
if (seekSlider.isValueChanging()) mediaPlayer.seek(Duration.seconds(seekSlider.getValue()));
});
mediaPlayer.setOnEndOfMedia(() -> mediaPlayer.stop());
}
private void updateTimeLabel() {
if (mediaPlayer == null) {
timeLabel.setText(“00:00 / 00:00”);
return;
}
Duration cur = mediaPlayer.getCurrentTime();
Duration total = mediaPlayer.getTotalDuration();
timeLabel.setText(formatTime(cur) + ” / “ + formatTime(total));
}
private String formatTime(Duration d) {
if (d == null || d.isUnknown()) return “00:00”;
int seconds = (int)Math.floor(d.toSeconds());
int mins = seconds / 60;
int secs = seconds % 60;
return String.format(”%02d:%02d”, mins, secs);
}
public static void main(String[] args) { launch(args); }
}
Notes on format support
- JavaFX Media supports MP3 and WAV in most builds. AAC/m4a may work depending on platform-specific codecs.
- For broader codec coverage (e.g., FLAC, OGG), integrate third-party libraries (vlcj for native FFMPEG integration, JLayer for MP3, or use a native process with ffmpeg).
Cross-platform packaging
- Use jlink to create a runtime image including the JRE and JavaFX modules for each target OS.
- Or use jpackage to create native installers (MSI/EXE for Windows, DMG for macOS, DEB/RPM for Linux). Include the javafx-media and javafx-controls modules.
- Remember classifiers: when bundling, include platform-specific JavaFX native libs.
Performance and reliability tips
- Dispose MediaPlayer instances after stopping to free native resources.
- Use a background thread for any heavy file scanning or metadata extraction.
- Test on each OS—audio codecs and signed media libraries can differ by platform.
Next steps / enhancements
- Add playlist support and shuffle/repeat modes.
- Show metadata (title, artist, album art) using Media.getMetadata().
- Implement gapless playback using two MediaPlayer instances and crossfading.
- Add keyboard shortcuts and drag-and-drop file support.
This is a complete minimal implementation to get a cross-platform Java audio player running with JavaFX; extend it with playlists, metadata display, and richer UI as needed.