
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.security.SecureRandom;
import java.util.ConcurrentModificationException;
public class MultipleBouncePane extends Application {
public void start(Stage primaryStage) throws Exception {
MultipleBallPane ballPane = new MultipleBallPane();
ballPane.setStyle("-fx-border-color: darkseagreen;");
Button suspend = new Button("Suspend");
Button resume = new Button("Resume");
Button add = new Button("+");
Button subtract = new Button("-");
HBox hBox = new HBox(10, suspend, resume, add, subtract);
hBox.setAlignment(Pos.CENTER);
suspend.setOnAction(event -> ballPane.pause());
resume.setOnAction(event -> ballPane.play());
add.setOnAction(event -> ballPane.add());
subtract.setOnAction(event -> ballPane.subtract());
ballPane.setOnMousePressed(event -> ballPane.pause());
ballPane.setOnMouseReleased(event -> ballPane.play());
ScrollBar speed = new ScrollBar();
ballPane.rateProperty().bind(speed.valueProperty());
BorderPane pane = new BorderPane(ballPane);
Scene scene = new Scene(pane, 350, 250);
primaryStage.setScene(scene);
primaryStage.setTitle("MultipleBouncePane");
private class MultipleBallPane extends Pane {
private Timeline animation;
public MultipleBallPane() {
animation = new Timeline(new KeyFrame(Duration.millis(50), event -> moveBall()));
animation.setCycleCount(Timeline.INDEFINITE);
Color color = new Color(Math.random(), Math.random(), Math.random(), 0.5);
Ball ball = new Ball(30, 30, 2+new SecureRandom().nextInt(19), color);
ball.setOnMousePressed(event -> {
getChildren().remove(ball);
if (getChildren().size() > 0) {
Ball temp = (Ball)getChildren().get(0);
for (Node node: getChildren()) {
if (((Ball)node).getRadius() > temp.getRadius())
getChildren().remove(temp);
public void increaseSpeed() {
animation.setRate(animation.getRate() + 0.1);
public void decreaseSpeed() {
animation.setRate(animation.getRate() <= 0 ? 0 : animation.getRate()-0.1);
public DoubleProperty rateProperty() {
return animation.rateProperty();
protected void moveBall() {
for (Node node : this.getChildren()) {
if (ball.getCenterX() < ball.getRadius() || ball.getCenterX() > getWidth()-ball.getRadius())
if (ball.getCenterY() < ball.getRadius() || ball.getCenterY() > getHeight()-ball.getRadius())
ball.setCenterX(ball.dx + ball.getCenterX());
ball.setCenterY(ball.dy + ball.getCenterY());
for (Node node1: this.getChildren()) {
Ball ball1 = (Ball)node1;
if (!ball1.equals(ball) && (new Point2D(ball.getCenterX(), ball.getCenterY()).distance(ball1.getCenterX(),
ball1.getCenterY()) <= ball.getRadius()+ball1.getRadius())) {
int index1 = getChildren().indexOf(ball);
int index2 = getChildren().indexOf(ball1);
ball.setRadius(ball.getRadius() + ball1.getRadius());
getChildren().remove(ball1);
ball1.setRadius(ball1.getRadius() + ball.getRadius());
getChildren().remove(ball);
} catch (ConcurrentModificationException ex) {
class Ball extends Circle {
private double dx = 1, dy = 1;
public Ball(double centerX, double centerY, double radius, Paint fill) {
super(centerX, centerY, radius, fill);

