Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Java  Module 18 – Desktop GUIs with JavaFX (and Swing for legacy code)

JavaFX animation, audio and video

Animate JavaFX nodes with transitions, Timeline and AnimationTimer, and play sound and video with Media, MediaPlayer, MediaView and AudioClip.

  • Intermediate
  • 24 minutes
  • Examples run with Java 25.0.4 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 8 of 24 minutes free
Free: Three ways to animate a node · Easing and timing, measured · Sound and video in four classes. With a pass: 2 in-depth parts and 3 interview answers, about 16 minutes.

What you will learn

  • Build node animations with transitions and Timeline key frames, and combine them in sequence or in parallel
  • Run per-frame logic with AnimationTimer using its timestamp, not an assumed frame rate
  • Use Media, MediaPlayer, MediaView and AudioClip to play audio and video, and handle unsupported files

Before you start

What you’ll learn · Before you start

What you will learn

  • Build node animations with transitions and Timeline key frames, and combine them in sequence or in parallel
  • Run per-frame logic with AnimationTimer using its timestamp, not an assumed frame rate
  • Use Media, MediaPlayer, MediaView and AudioClip to play audio and video, and handle unsupported files

Before you start

On this page

JavaFX animates a node by changing one of its properties, such as translateX, opacity or rotate, a little on every frame, and it plays audio and video through its javafx.media module. There are three ways to animate. A transition is a ready-made animation of one node, such as a TranslateTransition that slides a node 100 pixels to the right. A timeline moves any number of properties through key frames that you place on a time line. An animation timer calls your own code once per frame. Every JavaFX animation runs on the JavaFX Application Thread, and transitions and timelines follow the clock rather than counting frames. For sound and video, a Media object names a file or a URL, a MediaPlayer plays it, a MediaView shows its video, and an AudioClip plays short sounds such as clicks.

Three ways to animate a node

Tool You give it JavaFX works out A good fit
A transition: TranslateTransition, FadeTransition, RotateTransition, ScaleTransition and others one node, a duration and the value to reach every value in between moving, fading, turning or growing one node
Timeline key frames: at which moment which properties have which values, and what to run then the values between the key frames several properties at once, or actions at fixed times, such as a clock
AnimationTimer a handle(long now) method nothing: it calls you once per frame with the time games and simulations that compute every frame themselves

Transitions and timelines are both Animation objects, so they share the controls: play(), pause(), stop(), jumpTo(duration), a cycle count and a rate. An interpolator decides how fast the value moves at each moment: LINEAR keeps one speed, while the default of every transition, EASE_BOTH, starts and ends slowly. A SequentialTransition plays animations one after another, and a ParallelTransition plays them at the same time. A PauseTransition waits for a set time, which spaces out the steps of a sequence. Setting the cycle count repeats an animation, and autoReverse plays every second cycle backwards. A scene’s reduced-motion preference tells an application to keep movement to a minimum, for people who feel unwell when the screen moves.

Easing and timing, measured

The examples run as single source files on JDK 25 with the JavaFX 27 SDK unpacked beside them, and the JDK reads the JavaFX options from the argument file fx.args. The last two options draw without a screen; leave them out on your computer to see the windows:

fx.args: one option per line ARGS · fx.args
--module-path javafx-sdk-27/lib
--add-modules javafx.controls,javafx.media
--enable-native-access=javafx.graphics,javafx.media
-Dglass.platform=headless
-Dprism.order=sw

The first program puts a ball on a 400 ms move of 100 pixels and stops the animation at every 100 ms with jumpTo, for four interpolators. The helper FxRun starts JavaFX without an Application class; the launcher finds it in FxRun.java beside the program:

Easing.java and its helper

Easing.java

import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.scene.shape.Circle;
import javafx.util.Duration;

/** Where a 400 ms, 100-pixel move puts a ball every 100 ms, for four interpolators. */
public class Easing {
    public static void main(String[] args) throws Exception {
        FxRun.start();
        FxRun.onFx(() -> {
            System.out.printf("%-20s%8d%8d%8d%8d%8d%n", "time (ms)", 0, 100, 200, 300, 400);
            Interpolator[] curves = {null, Interpolator.LINEAR, Interpolator.EASE_IN, Interpolator.EASE_OUT};
            for (Interpolator curve : curves) {
                Circle ball = new Circle(10);
                TranslateTransition move = new TranslateTransition(Duration.millis(400), ball);
                move.setByX(100);
                if (curve != null) {
                    move.setInterpolator(curve);
                }
                move.play();
                move.pause(); // stopped here, jumpTo() moves the ball to any moment of the animation
                StringBuilder row = new StringBuilder();
                for (int ms = 0; ms <= 400; ms += 100) {
                    move.jumpTo(Duration.millis(ms));
                    row.append(String.format("%8.1f", ball.getTranslateX()));
                }
                String name = move.getInterpolator().toString().replace("Interpolator.", "");
                System.out.printf("%-20s%s%n", curve == null ? name + " (default)" : name, row);
            }
        });
        Platform.exit();
    }
}

FxRun.java

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javafx.application.Platform;

/** Starts JavaFX without an Application subclass and runs code on the JavaFX Application Thread. */
final class FxRun {
    private FxRun() {
    }

    /** Starts the JavaFX runtime and waits until its thread is running. */
    static void start() throws InterruptedException {
        CountDownLatch started = new CountDownLatch(1);
        Platform.startup(started::countDown);
        started.await();
    }

    /** Computes a value on the JavaFX Application Thread and returns it. */
    static <T> T call(java.util.function.Supplier<T> task) throws InterruptedException {
        Object[] result = new Object[1];
        onFx(() -> result[0] = task.get());
        @SuppressWarnings("unchecked")
        T value = (T) result[0];
        return value;
    }

    /** Runs the task on the JavaFX Application Thread and waits for it, at most 10 seconds. */
    static void onFx(Runnable task) throws InterruptedException {
        CountDownLatch done = new CountDownLatch(1);
        Platform.runLater(() -> {
            try {
                task.run();
            } finally {
                done.countDown();
            }
        });
        if (!done.await(10, TimeUnit.SECONDS)) {
            throw new IllegalStateException("the JavaFX Application Thread did not answer");
        }
    }
}
Where four interpolators put the ball Python · easing_session.py
from session import javafx, run

javafx()
run("""
java @fx.args Easing.java
""")

Output

$ java @fx.args Easing.java
time (ms)                  0     100     200     300     400
EASE_BOTH (default)      0.0    18.8    50.0    81.3   100.0
LINEAR                   0.0    25.0    50.0    75.0   100.0
EASE_IN                  0.0    16.7    44.4    72.2   100.0
EASE_OUT                 0.0    27.8    55.6    83.3   100.0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 easing_session.py

All four curves start at 0 and end at 100 pixels on time; they differ in between. After a quarter of the time, LINEAR has covered a quarter of the way, 25 pixels, while the default EASE_BOTH has covered only 18.8, because it spends the first fifth of the time speeding up. EASE_IN lags all the way and EASE_OUT leads all the way. Since JavaFX 26, Interpolator.ofSpline takes the same four numbers as a CSS cubic-bezier() curve, so a curve designed for the web works here too.

Cubic Bezier & CSS Easing Generator Shape an easing curve by dragging its two control points, then pass the same four numbers to Interpolator.ofSpline.

The second program times a transition, a timeline and an animation timer on a shown stage with the system clock:

Timings.java Java · Timings.java
import java.util.concurrent.CountDownLatch;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

/** A transition, a timeline and an animation timer on a shown stage, timed with the system clock. */
public class Timings {
    static long elapsedMs(long since) {
        return (System.nanoTime() - since) / 1_000_000;
    }

    public static void main(String[] args) throws Exception {
        FxRun.start();
        CountDownLatch finished = new CountDownLatch(3);
        FxRun.onFx(() -> {
            Circle ball = new Circle(20, 50, 10);
            Stage stage = new Stage();
            stage.setScene(new Scene(new Pane(ball), 300, 100));
            stage.show();

            // 1. A transition: 400 ms, 100 pixels to the right.
            TranslateTransition move = new TranslateTransition(Duration.millis(400), ball);
            move.setByX(100);
            long moveStart = System.nanoTime();
            move.setOnFinished(e -> {
                System.out.printf("TranslateTransition, 400 ms: finished after %d ms at translateX %.1f%n", elapsedMs(moveStart), ball.getTranslateX());
                finished.countDown();
            });
            move.play();

            // 2. A timeline: key frames at 100, 200, 300, 400 and 500 ms, each counting a tick.
            int[] ticks = {0};
            Timeline clock = new Timeline();
            for (int i = 1; i <= 5; i++) {
                clock.getKeyFrames().add(new KeyFrame(Duration.millis(100 * i), e -> ticks[0]++));
            }
            long clockStart = System.nanoTime();
            clock.setOnFinished(e -> {
                System.out.printf("Timeline, 5 key frames: %d ticks, finished after %d ms%n", ticks[0], elapsedMs(clockStart));
                finished.countDown();
            });
            clock.play();

            // 3. An animation timer: called once per frame; counts the frames of its first 500 ms.
            new AnimationTimer() {
                long first = -1;
                int frames;

                @Override
                public void handle(long now) {
                    if (first < 0) {
                        first = now;
                    }
                    frames++;
                    if (now - first >= 500_000_000L) {
                        stop();
                        System.out.printf("AnimationTimer: %d frames in %d ms%n", frames, (now - first) / 1_000_000);
                        finished.countDown();
                    }
                }
            }.start();
        });
        finished.await();
        Platform.exit();
    }
}
A transition, a timeline and an animation timer, timed Python · timings_session.py
from session import javafx, run

javafx()
run("""
java @fx.args Timings.java
""")

Output

$ java @fx.args Timings.java
TranslateTransition, 400 ms: finished after 412 ms at translateX 100.0
Timeline, 5 key frames: 5 ticks, finished after 503 ms
AnimationTimer: 31 frames in 501 ms

This output changes from run to run: The times and the number of frames change by a few milliseconds and a frame or two from run to run.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 timings_session.py

The transition and the timeline finished within a few milliseconds of 400 and 500 ms: a JavaFX animation follows the clock, and its last frame lands slightly after the moment it was waiting for. The animation timer saw about 30 frames in half a second, about 60 a second on this computer. A screen that refreshes 120 times a second, a busy laptop or a hidden window gives another number, so per-frame code must use the now timestamp it is given, never an assumed frame rate.

Sound and video in four classes

A file or URL becomes a Media object, a MediaPlayer plays it to a MediaView and the speakers, and an AudioClip plays short sounds directly.A file or URLfile:, http:, https:, jar:Mediaduration, size, tracksMediaPlayerplay, pause, seek, volumeUNKNOWN → READY → PLAYINGOutputAudioClipshort sounds,kept in memoryMediaViewshows the videoSpeakersnew Media(uri)new MediaPlayer(media)framessoundnew AudioClip(uri)play()

The four media classes and how sound and video flow between them

Text description of the diagram

The diagram runs from top to bottom.

1. "A file or URL" (file:, http:, https: or jar:) is the source. 2. "new Media(uri)" turns it into a Media object, which knows the duration, the size of the video and the tracks. 3. "new MediaPlayer(media)" creates the player, with play, pause, seek and volume. Its status goes from UNKNOWN to READY to PLAYING. 4. The player sends video frames to a MediaView, which shows them, and sound to the speakers.

A dashed path on the side shows the AudioClip: "new AudioClip(uri)" loads a short sound from the same kind of source and keeps it in memory, and play() sends it straight to the speakers.

A Media object is the source: its duration, the size of its video and its tracks. A MediaPlayer plays one Media and has the controls: play, pause, stop, seek and volume. A MediaView is a node that shows the video of a player; audio plays without one. An AudioClip loads a short sound into memory and plays it at once, as often as you like, even several times at the same moment. A player works asynchronously: right after new MediaPlayer(...) its status is UNKNOWN and the duration is unknown too, so code that needs them waits for onReady. When a player is no longer needed, dispose() frees what it holds.

JavaFX 27 plays WAV and AIFF files with uncompressed audio, MP3 files, and MP4 files, also named .m4a or .m4v, with H.264 or H.265 video and AAC audio, plus HTTP Live Streaming, from file:, http:, https: and jar: URLs. Audio in other formats, such as Ogg, FLAC or WebM, has to be converted first. A file in a format that JavaFX does not support makes new Media(...) throw a MediaException whose type, such as MEDIA_UNSUPPORTED, says why.

OGG / Opus to MP3 Converter Convert an Ogg recording to MP3 in the browser before a JavaFX player opens it.

India-specific: GTU Object Oriented Programming-I

GTU’s syllabus for subject 3140705 lists animation, and video and audio, in its two JavaFX units; this lesson covers both on the current JavaFX release. MySmartCoPilot is not affiliated with GTU, and the official syllabus decides what is examined.

Check yourself

4 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 4 A 400 ms TranslateTransition moves a node 100 pixels with the default interpolator. How many pixels has the node moved after 100 ms?

    Type a number.

    Show the answer to question 1

    Answer: 18.8 px (anything from 18.6 to 19 counts)

    The default interpolator of a transition is EASE_BOTH, which spends the first fifth of the time speeding up, so after a quarter of the time the node has covered 18.8 pixels, not the 25 that LINEAR gives.

  2. Question 2 of 4 A game moves 40 sprites by its own physics rules every frame. Which JavaFX tool fits?

    Choose one answer.

    Show the answer to question 2

    Answer: AnimationTimer, whose handle(now) method runs once per frame

    Physics computes new positions from the state of every sprite each frame, which is exactly what an AnimationTimer's handle method is for; transitions and timelines move values towards targets you know in advance.

  3. Question 3 of 4 Why should code in AnimationTimer.handle use its now argument instead of assuming 60 frames a second?

    Choose one answer.

    Show the answer to question 3

    Answer: The number of frames a second differs between screens and drops when a computer is busy.

    now is a timestamp in nanoseconds, the same for every timer in a frame. Screens refresh at different rates, and a slow frame makes JavaFX skip frames, so a step per frame changes the speed while a step scaled by the elapsed time does not.

  4. Question 4 of 4 Every press of a button should make a short click sound. Which class plays it?

    Choose one answer.

    Show the answer to question 4

    Answer: One AudioClip, loaded once and played at every press

    An AudioClip keeps a short sound in memory and plays it at once, even several times at the same moment. A MediaPlayer is for longer recordings, works asynchronously and costs more to create; a MediaView only shows video.

Composing and timing animations, measured Pro: opens with a Pro, Premium or Ultimate pass

Real screens chain animations, repeat them, and have to keep their speed on a slow computer. This part measures composed animations, reproduces the frame-rate bug with numbers, handles reduced motion and ends with an exercise.

In this part:

  • Sequences, parallels, cycles and rates, measured
  • Moving by the clock, with a slow frame reproduced
  • Reduced motion: what JavaFX 27 does, and what it leaves to you
  • The same curve on JavaFX 25 and JavaFX 27
  • Your turn: a traffic light that pauses

11 runnable examples (CheckTraffic.java, Java, Python, starter/TrafficLight.java) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Playing sound and video reliably, with failures reproduced Pro: opens with a Pro, Premium or Ultimate pass

Media code fails in ways that throw nothing: a player that never becomes ready, a sound that stops halfway, a seek bar that fights the user. This part reproduces each failure, measures what it costs and shows the fix.

In this part:

  • A MediaPlayer’s life, event by event
  • The player that stopped: garbage collection
  • Files that will not play
  • Sound effects: one AudioClip, not a player per click
  • A seek bar that does not fight the user
  • Video: a MediaView only shows what the player plays

12 runnable examples (Java, Player.java, Python, Wav.java) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher to mid · Warm-up

    When do you use a transition, a Timeline or an AnimationTimer in JavaFX?

    A transition animates one property of one node towards a value you know in advance, such as sliding a card with TranslateTransition or fading a message with FadeTransition, and JavaFX computes every value in between with an interpolator. A Timeline suits several properties at once or actions at fixed moments: its key frames say which values to reach at which time and which handler to run then, as in a clock or a traffic light. An AnimationTimer calls handle(now) once per frame and computes nothing for you, which suits games and simulations whose next state depends on the current one. Transitions and timelines are Animation objects with play, pause, stop, a rate and a cycle count; a timer can only start and stop. All three run on the JavaFX Application Thread.

  2. Mid to senior

    A JavaFX game moves the player 4 pixels in every AnimationTimer call. One tester says it crawls when many enemies are on screen; another, with a 120 Hz monitor, says it runs too fast. Explain both reports, fix the movement and say how you would test the fix.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A quiz app creates a new MediaPlayer for a buzzer sound on every wrong answer. In long sessions some buzzers never sound, and the thread count and memory of the process keep rising. Explain what is happening and redesign the sound code.

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    A lecture player lets students open any audio or video file they have. Some files never start and show no message; others show a stack trace. Design how the player opens a file, explains problems in plain words and never hangs.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • A transition animates one node, a Timeline works through key frames, and an AnimationTimer calls your code every frame.
  • Transitions and timelines follow the clock; a transition’s default interpolator, EASE_BOTH, starts and ends slowly.
  • Per-frame code scales its steps by the now timestamp, never by an assumed frame rate.
  • Media is the source, MediaPlayer plays it, MediaView shows its video, and AudioClip plays short sounds.
  • A player is asynchronous: wait for onReady.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.