title: Performance of JavaFx Gui vs Swing
body: <p>I wrote two simple programs, both draw the same Sierpinski Triangle:
  <a href="https://i.stack.imgur.com/QuLUv.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/QuLUv.jpg" alt="enter image description here"></a>
  <br>One program was implemented using swing, and one using javafx.
  There is a very significant performance difference, swing implementation being consistently much faster:<br><br>
  <a href="https://i.stack.imgur.com/HpexV.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/HpexV.jpg" alt="enter image description here"></a></p>
  
  <p><br>(In this test case : Swing over 1 sec. Javafx over 12 seconds) <br>Is it to be expected or is there something very wrong with my javafx implementation ? </p>
  
  <p><strong>Swing implementation</strong> </p>
  
  <pre><code>import java.awt.BorderLayout;
  import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.Font;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.Point;
  import java.awt.Polygon;
  import java.awt.image.BufferedImage;
  import javax.swing.JFrame;
  import javax.swing.JPanel;
  import javax.swing.SwingUtilities;
  
  public class SimpleSrpnskTriSw {
  
      private Triangles triPanel;
  
      SimpleSrpnskTriSw(int numberOfLevels){
  
          JFrame frame = new JFrame("Sierpinski Triangles (swing)");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
          triPanel = new Triangles();
          frame.add(triPanel, BorderLayout.CENTER);
          frame.pack();
  
          frame.setResizable(false);
          frame.setVisible(true);
  
          triPanel.draw(numberOfLevels);
      }
  
      class Triangles extends JPanel{
  
          private static final int PANEL_WIDTH =600, PANEL_HEIGHT = 600;
          private static final int TRI_WIDTH= 500, TRI_HEIGHT= 500;
          private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH)/2;
          private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT)/2;
  
          private int countTriangles;
          private long startTime;
          boolean working;
          private int numberOfLevels = 0;
  
          Triangles() {
  
              setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
              startTime = System.currentTimeMillis();
              countTriangles = 0;
              working = true;
              draw();
          }
  
          void draw(int numLevels) {
  
              numberOfLevels = numLevels;
              working = true;
              draw();
          }
  
          void draw() {
  
              startTime = System.currentTimeMillis();
              countTriangles = 0;
  
              SwingUtilities.invokeLater(new Runnable() {
                  @Override
                  public void run() {
  
                      repaint();
                  }
              });
          }
  
          @Override
          public void paintComponent(Graphics g) {
  
              super.paintComponent(g);
              g.setFont(new Font("Ariel", Font.PLAIN, 14));
  
              if(working) {
  
                  g.setColor(getBackground());
                  g.fillRect(0,0,PANEL_WIDTH,PANEL_HEIGHT);
                  g.setColor(getForeground());
  
                  g.drawString("Working.........", 15, 15);
                  working = false;
                  return;
              }
  
              if(numberOfLevels &lt;= 0 ) {
                  return;
              }
              Point top = new Point(PANEL_WIDTH/2, TOP_GAP);
              Point left = new Point(SIDE_GAP, TOP_GAP+ TRI_HEIGHT);
              Point right = new Point(SIDE_GAP + TRI_WIDTH, TOP_GAP+ TRI_HEIGHT);
  
              BufferedImage bi = getBufferedImage(top, left, right);
              Graphics2D g2d = (Graphics2D) g;
              g2d.drawImage(bi,0,0, this);
  
              g.drawString("Number of triangles: "+ countTriangles, 15, 15);
              g.drawString("Time : "+ (System.currentTimeMillis()- startTime)+ " mili seconds", 15, 35);
              g.drawString("Levels: "+ numberOfLevels, 15, 50);
          }
  
          private BufferedImage getBufferedImage(Point top, Point left, Point right) {
  
              BufferedImage bi = new BufferedImage(PANEL_WIDTH,PANEL_HEIGHT,
                                                              BufferedImage.TYPE_INT_ARGB);
              drawTriangle(bi, numberOfLevels, top, left, right);
              return bi;
          }
  
          private void drawTriangle(BufferedImage bi, int levels, Point top, Point left, Point right) {
  
              if(levels &lt; 0) {
                  return ;
              }
  
              countTriangles++;
              Graphics g = bi.getGraphics();
              g.setColor(Color.RED);
  
              Polygon tri = new Polygon();
              tri.addPoint(top.x, top.y);  //use top,left right rather than fixed points
              tri.addPoint(left.x, left.y);
              tri.addPoint(right.x, right.y);
              g.drawPolygon(tri);
  
              // Get the midpoint on each edge in the triangle
              Point p12 = midpoint(top, left);
              Point p23 = midpoint(left, right);
              Point p31 = midpoint(right, top);
  
              // recurse on 3 triangular areas
              drawTriangle(bi, levels - 1, top, p12, p31);
              drawTriangle(bi, levels - 1, p12, left, p23);
              drawTriangle(bi, levels - 1, p31, p23, right);
          }
  
          private Point midpoint(Point p1, Point p2) {
  
              return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
          }
      }
  
      public static void main(String[] args) {
  
          new SimpleSrpnskTriSw(13);
      }
  }
  </code></pre>
  
  <p><strong>JavaFx implementation</strong></p>
  
  <pre><code>import javafx.application.Application;
  import javafx.application.Platform;
  import javafx.concurrent.Task;
  import javafx.geometry.Insets;
  import javafx.geometry.Point2D;
  import javafx.geometry.Pos;
  import javafx.scene.Scene;
  import javafx.scene.canvas.Canvas;
  import javafx.scene.canvas.GraphicsContext;
  import javafx.scene.image.WritableImage;
  import javafx.scene.layout.AnchorPane;
  import javafx.scene.layout.BorderPane;
  import javafx.scene.layout.Pane;
  import javafx.scene.paint.Color;
  import javafx.stage.Stage;
  
  public class SimpleSrpnskTriFx extends Application {
  
      private final int PADDING = 5;
      private static int numberOfLevels;
  
      public static void launch(String... args){
  
          numberOfLevels = 8;
  
          if((args != null) &amp;&amp; (args.length &gt; 0)) {
  
              try {
                  int num = Integer.parseInt(args[0]);
                  numberOfLevels =  num ;
              } catch (NumberFormatException ex) {
                  ex.printStackTrace();
                  return;
              }
          }
  
          Application.launch(args);
      }
  
      @Override
      public void start(Stage stage) {
  
          stage.setOnCloseRequest((ae) -&gt; {
              Platform.exit();
              System.exit(0);
          });
  
          stage.setTitle("Sierpinski Triangles (fx)");
  
          BorderPane mainPane = new BorderPane();
          mainPane.setPadding(new Insets(PADDING));
  
          Pane triPanel = new Triangles();
  
          BorderPane.setAlignment(triPanel, Pos.CENTER);
          mainPane.setCenter(triPanel);
  
          Scene scene = new Scene(mainPane);
  
          stage.setScene(scene);
          stage.centerOnScreen();
  
          stage.setResizable(false);
          stage.show();
      }
  
      class Triangles extends AnchorPane{
  
          private static final int PANEL_WIDTH =600, PANEL_HEIGHT = 600;
          private static final int TRI_WIDTH= 500, TRI_HEIGHT= 500;
          private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH)/2;
          private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT)/2;
          private int countTriangles;
          private long startTime;
          private Point2D top, left, right;
  
          private Canvas canvas;
          private Canvas backgroundCanvas;
          private GraphicsContext gc;
  
          Triangles(){
  
              setPrefSize(PANEL_WIDTH, PANEL_HEIGHT);
  
              canvas = getCanvas();
              backgroundCanvas = getCanvas();
              gc = backgroundCanvas.getGraphicsContext2D();
              getChildren().add(canvas);
              draw(numberOfLevels);
          }
  
          void draw(int numberLevels) {
  
              Platform.runLater(new Runnable() {
  
                  @Override
                  public void run() {
  
                      canvas.getGraphicsContext2D().fillText("Working....",5,15);
                      setStartPoints();
  
                      startTime = System.currentTimeMillis();
                      countTriangles = 0;
  
                      RunTask task = new RunTask(numberLevels, top, left, right);
                      Thread thread = new Thread(task);
                      thread.setDaemon(true);
                      thread.start();
                  }
              });
          }
  
          private void drawTriangle( int levels, Point2D top, Point2D left, Point2D right) {
  
              if(levels &lt; 0) {//add stop criteria
                  return ;
              }
  
              gc.strokePolygon( //implementing with strokeLine did not make much difference
                      new double[]{
                              top.getX(),left.getX(),right.getX()
                      },
                      new double[]{
                              top.getY(),left.getY(), right.getY()
                      },
                      3);
  
              countTriangles++;
  
              //Get the midpoint on each edge in the triangle
              Point2D p12 = midpoint(top, left);
              Point2D p23 = midpoint(left, right);
              Point2D p31 = midpoint(right, top);
  
              // recurse on 3 triangular areas
              drawTriangle(levels - 1, top, p12, p31);
              drawTriangle(levels - 1, p12, left, p23);
              drawTriangle(levels - 1, p31, p23, right);
          }
  
          private void setStartPoints() {
  
              top = new Point2D(getPrefWidth()/2, TOP_GAP);
              left = new Point2D(SIDE_GAP, TOP_GAP + TRI_HEIGHT);
              right = new Point2D(SIDE_GAP + TRI_WIDTH, TOP_GAP + TRI_WIDTH);
          }
  
          private Point2D midpoint(Point2D p1, Point2D p2) {
  
              return new Point2D((p1.getX() + p2.getX()) /
                      2, (p1.getY() + p2.getY()) / 2);
          }
  
          private void updateGraphics(boolean success){
  
              if(success) {
  
                  copyCanvas();
  
                  GraphicsContext gc = canvas.getGraphicsContext2D();
                  gc.fillText("Number of triangles: "+ countTriangles,5,15);
                  gc.fillText("Time : "+ (System.currentTimeMillis()- startTime )+ " mili seconds", 5,35);
                  gc.fillText("Levels: "+ numberOfLevels,5,55);
              }
  
          }
  
          private Canvas getCanvas() {
  
              Canvas canvas = new Canvas();
              canvas.widthProperty().bind(widthProperty());
              canvas.heightProperty().bind(heightProperty());
              canvas.getGraphicsContext2D().setStroke(Color.RED);
              canvas.getGraphicsContext2D().setLineWidth(0.3f);
  
              return canvas;
          }
  
          private void copyCanvas() {
  
              WritableImage image = backgroundCanvas.snapshot(null, null);
              canvas.getGraphicsContext2D().drawImage(image, 0, 0);
          }
  
          /**
           */
          class RunTask extends Task&lt;Void&gt;{
  
              private int levels;
              private Point2D top, left;
              private Point2D right;
  
              RunTask(int levels, Point2D top, Point2D left, Point2D right){
  
                  this.levels = levels;
                  this.top = top;
                  this.left = left;
                  this.right = right;
  
                  startTime = System.currentTimeMillis();
                  countTriangles = 0;
              }
  
              @Override public Void call() {
  
                  drawTriangle(levels,top, left, right);
                  return null;
              }
  
              @Override
              protected void succeeded() {
  
                  updateGraphics(true);
                  super.succeeded();
              }
  
              @Override
              protected void failed() {
  
                  updateGraphics(false);
              }
          }
      }
  
      public static void main(String[] args) {
          launch("13");
      }
  }
  </code></pre>