Up to this point, the graphics drawn by our applets have been relatively simple. With more complex graphics however, whether in animations or interactive programs, flicker can become a problem. (You may have already noticed subtle flickering in some of the previous applets.)

This example demonstrate the problem. It uses a pseudo-random number generator to produce a big, hairy tangle of lines. The lines follow the mouse cursor.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.Math;

public class NoBackbuffer1 extends Applet
   implements MouseMotionListener {

   int width, height;
   int mx, my;  // the mouse coordinates
   Point[] points;
   int N = 300;

   public void init() {
      width = getSize().width;
      height = getSize().height;
      setBackground( Color.black );

      mx = width/2;
      my = height/2;

      points = new Point[ N ];
      for ( int i = 0; i < N; ++i ) {
         int x = (int)(( Math.random() - 0.5 ) * width / 1.5);
         int y = (int)(( Math.random() - 0.5 ) * height / 1.5);
         points[i] = new Point( x, y );
      }

      addMouseMotionListener( this );
   }

   public void mouseMoved( MouseEvent e ) {
      mx = e.getX();
      my = e.getY();
      showStatus( "Mouse at (" + mx + "," + my + ")" );
      repaint();
      e.consume();
   }
   public void mouseDragged( MouseEvent e ) { }

   public void paint( Graphics g ) {
      g.setColor( Color.white );
      for ( int j = 1; j < N; ++j ) {
         Point A = points[j-1];
         Point B = points[j];
         g.drawLine( mx+A.x, my+A.y, mx+B.x, my+B.y );
      }
   }
}

This second example makes the problem even more pronounced by rendering a bitmap image in the background.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.Math;

public class NoBackbuffer2 extends Applet
   implements MouseMotionListener {

   int width, height;
   int mx, my;  // the mouse coordinates
   Point[] points;
   int N = 300;
   Image img;

   public void init() {
      width = getSize().width;
      height = getSize().height;
      setBackground( Color.black );

      mx = width/2;
      my = height/2;

      points = new Point[ N ];
      for ( int i = 0; i < N; ++i ) {
         int x = (int)(( Math.random() - 0.5 ) * width / 1.5);
         int y = (int)(( Math.random() - 0.5 ) * height / 1.5);
         points[i] = new Point( x, y );
      }

      // Download the image "fractal.gif" from the
      // same directory that the applet resides in.
      img = getImage( getDocumentBase(), "fractal.gif" );

      addMouseMotionListener( this );
   }

   public void mouseMoved( MouseEvent e ) {
      mx = e.getX();
      my = e.getY();
      showStatus( "Mouse at (" + mx + "," + my + ")" );
      repaint();
      e.consume();
   }
   public void mouseDragged( MouseEvent e ) { }

   public void paint( Graphics g ) {
      g.drawImage( img, 0, 0, this );
      g.setColor( Color.white );
      for ( int j = 1; j < N; ++j ) {
         Point A = points[j-1];
         Point B = points[j];
         g.drawLine( mx+A.x, my+A.y, mx+B.x, my+B.y );
      }
   }
}

The solution is to use double-buffering : rather than perform drawing operations directly to screen, we draw onto an image buffer (the “backbuffer”) in memory, and only after completing this image do we copy it onto the screen. There is no need to erase or clear the contents of the screen before copying (or “swapping”, as it’s called) the backbuffer onto the screen. During the swap, we simply overwrite the image on the screen. Hence the screen never displays a partial image: even in the middle of swapping, the screen will contain 50 % of the old image and 50 % of the new image. As long as the swap is not too slow, the eye is fooled into seeing a continuous, smooth flow of images.

This example uses a backbuffer.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.Math;

public class Backbuffer1 extends Applet
   implements MouseMotionListener {

   int width, height;
   int mx, my;  // the mouse coordinates
   Point[] points;
   int N = 300;
   Image img;
   Image backbuffer;
   Graphics backg;

   public void init() {
      width = getSize().width;
      height = getSize().height;
      setBackground( Color.black );

      mx = width/2;
      my = height/2;

      points = new Point[ N ];
      for ( int i = 0; i < N; ++i ) {
         int x = (int)(( Math.random() - 0.5 ) * width / 1.5);
         int y = (int)(( Math.random() - 0.5 ) * height / 1.5);
         points[i] = new Point( x, y );
      }

      img = getImage(getDocumentBase(), "fractal.gif");

      backbuffer = createImage( width, height );
      backg = backbuffer.getGraphics();
      backg.setColor( Color.white );

      addMouseMotionListener( this );
   }

   public void mouseMoved( MouseEvent e ) {
      mx = e.getX();
      my = e.getY();
      showStatus( "Mouse at (" + mx + "," + my + ")" );

      backg.drawImage( img, 0, 0, this );
      for ( int j = 1; j < N; ++j ) {
         Point A = points[j-1];
         Point B = points[j];
         backg.drawLine( mx+A.x, my+A.y, mx+B.x, my+B.y );
      }

      repaint();
      e.consume();
   }
   public void mouseDragged( MouseEvent e ) { }

   public void paint( Graphics g ) {
      g.drawImage( backbuffer, 0, 0, this );
   }
}