| This code was written with the _ALPHA_ version of Processing and it may not run correctly with the current vesion.
    // The Internet Age
    // or The Rights of Spring
    // Reed Kram

    // Created    October 2002
    // Revised    November 2002

    class particle {
      float x, y, z;
      float Vx, Vy, Vz;
      float Ax, Ay, Az;
      float mass, drag;

      particle(float theMass, float theDrag, float startX,
      float startY, float startZ) {
        x = startX;
        y = startY;
        z = startZ;
        Vx = 0.0f;
        Vy = 0.0f;
        Vz = 0.0f;
        Ax = 0.0f;
        Ay = 0.0f;
        Az = 0.0f;
        mass = theMass;
        drag = theDrag;
      }

      // iterate for a single force acting on the particle
      void iterate(float Fkx, float Fky, float Fkz) {
        float Fx, Fy, Fz;

        Fx = Fkx;
        Fy = Fky;
        Fz = Fkz;
        Ax=Fx/mass;
        Ay=Fy/mass;
        Az=Fz/mass;

        Vx += Ax;
        Vy += Ay;
        Vz += Az;
        Vx *= (1.0f-drag);
        Vy *= (1.0f-drag);
        Vz *= (1.0f-drag);

        x += Vx;
        y += Vy;
        z += Vz;
      }
    }

    class imageDownloader implements Runnable {
      Vector imagesToDownload;
      Vector inbox;
      Thread runner;
      BApplet parent;

      public imageDownloader(BApplet parent) {
        this.parent = parent;
        imagesToDownload = new Vector();
        inbox = new Vector();

        runner = new Thread(this);
      }

      public void downloadFile(String filename) {
        imagesToDownload.addElement(filename);
        if (!runner.isAlive()) {
          runner.start();
        }
      }

      public Vector fetchDownloadedImages() {
        Vector theInbox = (Vector) inbox.clone();
        if (inbox != null) {
          inbox.removeAllElements();
        }
        if (imagesToDownload.size() < 1 && runner.isAlive()) {
          runner.stop();  // done downloading files...
        }
        return theInbox;
      }

      public void run() {
        Toolkit tk = Toolkit.getDefaultToolkit();
        Image imageToDownload = null;
        while(true) {
          try {
            if ((imageToDownload != null) && ((tk.checkImage(imageToDownload, -1, -1, null) & ImageObserver.ALLBITS) != 0)) {  // the image has downloaded fully
              int imageWidth = imageToDownload.getWidth(null);
              for (int i=0; i<imageToDownload.getHeight(null) / imageWidth; i++) {
                Image newImage = parent.createImage(imageWidth, imageWidth);
                if (newImage == null) {
                  System.out.println("unable to allocate offscreen graphics. need more memory.");
                  break;
                }
                Graphics newImageGraphics = newImage.getGraphics();
                newImageGraphics.drawImage(imageToDownload, 0, 0, 16, 16, 0, i*imageWidth, 16, (i+1)*imageWidth -1, null);
                inbox.addElement(newImage);
              }
              imageToDownload = null;
            }
            Thread.sleep(200);
            if (imageToDownload == null && imagesToDownload.size() > 0) {
              String imageFilename = (String) imagesToDownload.elementAt(0);
              imageToDownload = tk.getImage(getClass().getResource(imageFilename));
              tk.prepareImage(imageToDownload, -1, -1, null);
              imagesToDownload.removeElementAt(0);
            }
          } catch (Exception e) {
            System.out.println("downloading image >>>>"+e.toString()); e.printStackTrace(System.out);
          }
        }
      }
    }

    String logo[] = {"**",
              "**",
              "**",
              "**  ** ** ** ****  ****** ***",
              "** **  *****  ** **   **  **",
              "****   **  ***** **   **  **",
              "** **  **   **  ** **   **  **",
              "**  ** **  ***** **   **  **"};

    static int MAX_NUM_IMAGES = 300;
    static int NUM_IMAGE_FILES = 16;
    imageDownloader theDownloader;
    Vector frameImages = new Vector();
    static int frameImageWidth = 16;
    static int frameImageHeight = 16;
    particle pixelParticles[][] = new particle[frameImageWidth][frameImageHeight];

    int framePixels[] = new int[frameImageWidth * frameImageHeight];
    Image frameOffImage;
    Graphics frameOffGraphics;
    PixelGrabber framePixelGrabber;

    int numImagesLoaded = 0;
    int thisImageIndex = 0;
    long lastFrameTime = 0;

    particle cameraRotation = new particle(3, 0.7f, PI, 1.0f, 1.0f);
    float cameraRotationDestination[] = { 0.0f, 0.0f, 0.0f };

    long creditTextTimer;

    void setup()
    {
      size(300, 300);
      background(49, 81, 74);
      noStroke();
      colorMode(RGB, 100);

      for (int i=0; i<frameImageWidth; i++) {
        for (int j=0; j<frameImageHeight; j++) {
          pixelParticles[i][j] = new particle(4.0f, 0.8f, 0.0f, 0.0f, 0.0f);
        }
      }

      creditTextTimer = millis();
    }

    void mousePressed() {
      cameraRotationDestination[0] = (mouseX - width/2) / 2.0f * (PI/180.0f);
      cameraRotationDestination[1] = (mouseY - height/2) / 2.0f * (PI/180.0f);
      cameraRotationDestination[2] = 100;
    }

    void mouseDragged() {
      cameraRotationDestination[0] = (mouseX - width/2) / 2.0f * (PI/180.0f);
      cameraRotationDestination[1] = (mouseY - height/2) / 2.0f * (PI/180.0f);
      cameraRotationDestination[2] = 100;
    }

    void mouseReleased() {
      cameraRotationDestination[0] = 0;
      cameraRotationDestination[1] = 0;
      cameraRotationDestination[2] = 0;
    }

    void drawBoxTop(float x1, float x2,
                    float y1, float y2,
                    float z1, float z2) {
      beginShape(QUADS);
      vertex(x1, y2, z2); // draw front
      vertex(x1, y1, z2);
      vertex(x2, y1, z2);
      vertex(x2, y2, z2);
      vertex(x2, y2, z2); // draw right side
      vertex(x2, y1, z2);
      vertex(x2, y1, z1);
      vertex(x2, y2, z1);
      vertex(x2, y2, z1); // draw back
      vertex(x2, y1, z1);
      vertex(x1, y1, z1);
      vertex(x1, y2, z1);
      vertex(x1, y2, z1); // draw left side
      vertex(x1, y1, z1);
      vertex(x1, y1, z2);
      vertex(x1, y2, z2);
      endShape();
    }

    static Color
    getPixelColor(int pixels[], int X, int Y, int w) {
      int pixel = pixels[Y*w + X];

      int alpha = (pixel >> 24) & 0xff;
      int red   = (pixel >> 16) & 0xff;
      int green = (pixel >>  8) & 0xff;
      int blue  = (pixel    ) & 0xff;

      return new Color(red, green, blue);
    }

    void drawLogo(int x, int y) {
      int c = color(100, 100, 100);
      for (int i=0; i<logo.length; i++) {
        String thisLine = logo[i];
        for (int j=max(0, -x); j<thisLine.length(); j++) {
          if (thisLine.charAt(j) != ' ') {
            setPixel(x + j, y + i, c);
          }
        }
      }
    }

    void loop()
    {
      if (frameOffImage == null || frameOffGraphics == null) {
        theDownloader = new imageDownloader(this);
        for (int i=0; i<NUM_IMAGE_FILES; i++) {
          theDownloader.downloadFile("media/images"+i+".jpg");
        }

        frameOffImage = createImage(frameImageWidth, frameImageHeight);
        frameOffGraphics = frameOffImage.getGraphics();
        creditTextTimer = millis();
      }

      cameraRotation.iterate(cameraRotationDestination[0] - cameraRotation.x,
                   cameraRotationDestination[1] - cameraRotation.y,
                   cameraRotationDestination[2] - cameraRotation.z);

      long curTime = millis();

      Vector downloadedImages = theDownloader.fetchDownloadedImages();
      if (downloadedImages != null) {
        for (int i=0; i<downloadedImages.size(); i++) {
          frameImages.addElement(downloadedImages.elementAt(i));
        }
      }
      if (curTime - lastFrameTime > 450) {
        if ((thisImageIndex < MAX_NUM_IMAGES-1) && (frameImages.size() > thisImageIndex+1) && ((checkImage((Image) frameImages.elementAt(thisImageIndex+1), null) & ImageObserver.ALLBITS) != 0)) {
          thisImageIndex++;
        } else {
          thisImageIndex = 0;
        }
        lastFrameTime = curTime;
      }

      if (frameImages.size() < 1 || !((checkImage((Image) frameImages.elementAt(thisImageIndex), null) & ImageObserver.ALLBITS) != 0)) {
        return;
      }
         frameOffGraphics.drawImage((Image) frameImages.elementAt(thisImageIndex), 0, 0, null);

      framePixelGrabber = new PixelGrabber(frameOffImage, 0, 0, frameImageWidth, frameImageHeight, framePixels, 0, frameImageWidth);
      try {
        framePixelGrabber.grabPixels();
      } catch (InterruptedException e) {
        System.err.println("interrupted waiting for pixels!");
        return;
      } catch (Exception e) { System.out.println("grabbing pixels>>>>" + e.toString()); e.printStackTrace(System.out); }
      if ((framePixelGrabber.getStatus() & ImageObserver.ABORT) != 0) {
        System.err.println("image fetch aborted or errored");
        return;
      }

      for (int X = 0; X < frameImageWidth-1; X++) {
        for (int Y = 0; Y < frameImageHeight-1; Y++) {
          Color pixelColor = getPixelColor(framePixels, X, Y, frameImageWidth);
          pixelParticles[X][Y].iterate(
            pixelColor.getRed()*100/255 - pixelParticles[X][Y].x,
            pixelColor.getGreen()*100/255 - pixelParticles[X][Y].y,
            pixelColor.getBlue()*100/255 - pixelParticles[X][Y].z);
        }
      }

      push();

      translate(width/2, height/2, 0);

      rotateY( cameraRotation.x);
      rotateX(-cameraRotation.y);

      float xOffset = frameImageWidth/2.0f -1;
      float yOffset = frameImageHeight/2.0f -1;
      float scale = 4.0f;
      for (int X = 0; X < frameImageWidth-1; X++) {
        for (int Y = 0; Y < frameImageHeight-1; Y++) {
          float x = 3*(X - xOffset);
          float y = 3*(Y - yOffset);
          int pixelColor = color(pixelParticles[X][Y].x, pixelParticles[X][Y].y, pixelParticles[X][Y].z);
          float z = red(pixelColor)/6.0f;

          push();
          translate(scale*x, scale*y);
          fill(red(pixelColor), green(pixelColor), blue(pixelColor));
          drawBoxTop(-scale, scale,
                 -scale, scale,
                 -scale*z, scale*z);
          pop();
        }
      }
      pop();

      if (curTime - creditTextTimer <= 15000) {
        drawLogo(5, height-5 -8);
      } else if (curTime - creditTextTimer < 18000) {
          drawLogo((int)(5 - 60 * (curTime - creditTextTimer - 15000)/700.0), height-5 -8);
      }
    }