AboutBlogContact
Backend EngineeringApril 18, 1999 8 min read 120Updated: May 18, 2026

Java Applets: Adding Real Interactivity to Web Pages (1999)

AunimedaAunimeda
📋 Table of Contents

Java Applets: Adding Real Interactivity to Web Pages (1999)

Sun Microsystems launched Java in 1995 with a specific promise: "Write Once, Run Anywhere." For web development, this meant Java Applets - compiled Java programs that ran inside the browser via a Java Virtual Machine plugin. The pitch was compelling: full graphics, animation, sound, networking, and user interaction, all inside a <applet> tag on an ordinary HTML page.

In 1999, if you needed genuine interactive graphics - a real-time chart, a draggable diagram, an animated visualization - Java Applets were the only serious option. JavaScript was limited to form validation and simple DOM manipulation. Flash existed but was proprietary and expensive. Java was open, standardized, and backed by Sun.

We built applets. Here is what that looked like.


The Architecture: JDK 1.1 and AWT

Java Applets used the Abstract Window Toolkit (AWT) for graphics and UI. The model was event-driven: the browser called your applet's lifecycle methods (init(), start(), stop(), destroy()), and you overrode paint() to draw to the screen.

// StockTickerApplet.java - JDK 1.1, circa 1999
// A scrolling stock ticker - one of the classic 1999 applet use cases

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class StockTickerApplet extends Applet implements Runnable {
    
    // Stock data passed from the HTML page via <param> tags
    private String tickerText = "MSFT 89.50 (+1.25)  AAPL 28.75 (-0.50)  SUNW 42.00 (+2.00)  IBM 134.25 (+0.75)";
    private int    scrollSpeed = 2;      // pixels per frame
    private Color  bgColor     = Color.black;
    private Color  textColor   = Color.green;   // green-on-black: very 1999
    
    private Thread animationThread;
    private int    xPosition;
    private int    textWidth;
    private Font   tickerFont;
    
    @Override
    public void init() {
        // Read parameters from <param> tags in HTML
        String paramText  = getParameter("text");
        String paramSpeed = getParameter("speed");
        
        if (paramText  != null) tickerText  = paramText;
        if (paramSpeed != null) scrollSpeed = Integer.parseInt(paramSpeed);
        
        tickerFont = new Font("Monospaced", Font.BOLD, 14);
        
        // Set applet background
        setBackground(bgColor);
        
        // Measure how wide the text will be
        FontMetrics fm = getFontMetrics(tickerFont);
        textWidth  = fm.stringWidth(tickerText);
        xPosition  = getSize().width;   // start off screen to the right
    }
    
    @Override
    public void start() {
        // Called when the browser tab becomes visible
        if (animationThread == null || !animationThread.isAlive()) {
            animationThread = new Thread(this);
            animationThread.start();
        }
    }
    
    @Override
    public void stop() {
        // Called when the user navigates away - stop wasting CPU
        if (animationThread != null) {
            animationThread = null;
        }
    }
    
    @Override
    public void run() {
        // Animation loop - runs in its own thread
        while (Thread.currentThread() == animationThread) {
            xPosition -= scrollSpeed;
            
            // Reset when text has scrolled fully off screen
            if (xPosition < -textWidth) {
                xPosition = getSize().width;
            }
            
            repaint();  // triggers paint()
            
            try {
                Thread.sleep(40);   // ~25 frames per second
            } catch (InterruptedException e) {
                break;
            }
        }
    }
    
    @Override
    public void paint(Graphics g) {
        // AWT Graphics - the 1999 way to draw
        g.setFont(tickerFont);
        g.setColor(textColor);
        g.drawString(tickerText, xPosition, getSize().height / 2 + 5);
    }
    
    // Double-buffering to eliminate flicker - mandatory in 1999 AWT
    // Without this, every repaint() causes a visible flash
    private Image    offscreenImage;
    private Graphics offscreenGraphics;
    
    @Override
    public void update(Graphics g) {
        Dimension d = getSize();
        if (offscreenImage == null) {
            offscreenImage    = createImage(d.width, d.height);
            offscreenGraphics = offscreenImage.getGraphics();
        }
        // Draw everything off-screen first
        offscreenGraphics.setColor(bgColor);
        offscreenGraphics.fillRect(0, 0, d.width, d.height);
        paint(offscreenGraphics);
        // Then blit to screen in one operation
        g.drawImage(offscreenImage, 0, 0, this);
    }
}

Compile and deploy:

# JDK 1.1 compilation
javac StockTickerApplet.java

# This produces: StockTickerApplet.class
# Bundle into a JAR for faster download (one HTTP request instead of one per class):
jar cvf ticker.jar StockTickerApplet.class

Embed in HTML:

<!-- HTML 4.0 applet tag - deprecated in HTML 4.01 but universally used -->
<applet code="StockTickerApplet.class"
        archive="ticker.jar"
        width="500"
        height="30"
        alt="Stock ticker requires Java">
  <param name="text"  value="MSFT 89.50 (+1.25)  AAPL 28.75 (-0.50)  IBM 134.25 (+0.75)">
  <param name="speed" value="3">
  <!-- Fallback for browsers without Java plugin -->
  <p>Your browser does not support Java applets.
  <a href="ticker.html">View static version</a></p>
</applet>

A More Complex Example: Interactive Chart

// BarChartApplet.java - interactive bar chart, JDK 1.1 / AWT
// Bars highlight on mouseover - something impossible with 1999 JavaScript

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class BarChartApplet extends Applet implements MouseMotionListener {
    
    private String[] labels = {"Jan", "Feb", "Mar", "Apr", "May", "Jun"};
    private int[]    values = {42, 67, 55, 89, 73, 91};
    private int      hoveredBar = -1;
    
    @Override
    public void init() {
        addMouseMotionListener(this);
        setBackground(Color.white);
        
        // Parse values from <param> tags
        String paramValues = getParameter("values");
        if (paramValues != null) {
            String[] parts = paramValues.split(",");
            values = new int[parts.length];
            for (int i = 0; i < parts.length; i++) {
                values[i] = Integer.parseInt(parts[i].trim());
            }
        }
    }
    
    @Override
    public void paint(Graphics g) {
        int w      = getSize().width;
        int h      = getSize().height;
        int margin = 30;
        int barW   = (w - 2 * margin) / values.length - 4;
        int maxVal = 0;
        for (int v : values) if (v > maxVal) maxVal = v;
        
        // Draw axes
        g.setColor(Color.black);
        g.drawLine(margin, h - margin, w - margin, h - margin);  // X axis
        g.drawLine(margin, margin, margin, h - margin);           // Y axis
        
        // Draw bars
        for (int i = 0; i < values.length; i++) {
            int barH = (int)((double)values[i] / maxVal * (h - 2 * margin));
            int x    = margin + i * (barW + 4) + 4;
            int y    = h - margin - barH;
            
            // Highlight hovered bar
            g.setColor(i == hoveredBar ? Color.orange : new Color(51, 102, 204));
            g.fillRect(x, y, barW, barH);
            g.setColor(Color.black);
            g.drawRect(x, y, barW, barH);
            
            // Label
            g.setFont(new Font("SansSerif", Font.PLAIN, 10));
            g.drawString(labels[i < labels.length ? i : 0], x, h - 15);
            
            // Value above bar
            g.drawString(String.valueOf(values[i]), x, y - 2);
        }
    }
    
    @Override
    public void mouseMoved(MouseEvent e) {
        int w      = getSize().width;
        int h      = getSize().height;
        int margin = 30;
        int barW   = (w - 2 * margin) / values.length - 4;
        
        int newHovered = -1;
        for (int i = 0; i < values.length; i++) {
            int x = margin + i * (barW + 4) + 4;
            if (e.getX() >= x && e.getX() <= x + barW) {
                newHovered = i;
                break;
            }
        }
        if (newHovered != hoveredBar) {
            hoveredBar = newHovered;
            repaint();
        }
    }
    
    @Override public void mouseDragged(MouseEvent e) {}
}

This was genuinely impressive in 1999. A bar chart that highlighted on mouseover - in JavaScript you could not do this at all. The best you could do with JavaScript was swap <img> tags for rollover effects. Java let you write real interactive graphics code.


The Problems That Killed Applets

Startup time. The JVM plugin had to load before any applet code ran. On a 56k modem, users stared at a grey box for 10-30 seconds waiting for the JVM to initialize. Many users clicked away before seeing anything.

Version fragmentation. IE shipped its own Microsoft JVM. Netscape used the Sun JVM plugin. Java 1.0, 1.1, and 1.2 had incompatible APIs. An applet that worked perfectly in IE/Windows would fail in Netscape/Mac or vice versa. The "Write Once, Run Anywhere" promise broke down on different browser/OS combinations constantly.

// Code that exposed JVM version differences:
// java.awt.Color constructor behavior differed between JDK 1.0 and 1.1
// Netscape's JVM implementation of Thread.sleep() was buggy in some versions
// Microsoft's JVM omitted some RMI classes entirely

// We added version detection:
String javaVersion = System.getProperty("java.version");
// Then branched on the version - defeating the entire WORA premise

Security restrictions. Applets ran in a sandbox. They could not read local files, write to the filesystem, or connect to servers other than the one they were loaded from. This was correct for security, but it meant applets could not be used as general-purpose applications without complex workarounds (signed applets, security policy files).

Flash was shinier. Macromedia Flash 4 (1999) and Flash 5 (2000) offered vector animation, streaming audio, and ActionScript programming with a dramatically better authoring experience. Flash loaded faster due to its binary format, required no JVM, and let designers (not just Java programmers) create interactive content. By 2001, Flash had won the "interactive web" category.


What Java Applets Established

Despite failing as a mainstream web technology, Java Applets demonstrated that the browser could be a platform for real application development - not just document display. This idea persisted and evolved: Flash, then Silverlight, then HTML5 Canvas, WebGL, and WebAssembly all pursued the same goal of running real programs in the browser.

The architectural patterns of applet development - lifecycle methods (init/start/stop/destroy), event listeners, double-buffered rendering - appear in modified form in every UI framework that followed. Android's Activity lifecycle is a direct descendant of the Java Applet lifecycle. The browser component model that React and Web Components embody follows the same init/render/destroy pattern.

The failure was instructive: "Write Once, Run Anywhere" requires not just language portability but runtime portability. The JVM was not identical across browser vendors and operating systems. The lesson - that the runtime environment must be standardized, not just the language - drove the eventual standardization of JavaScript engines and the decision to build the modern web platform around a single, agreed-upon runtime.


Aunimeda builds production-grade backend systems - APIs, microservices, real-time applications, and system integrations.

Contact us for backend engineering services. See also: Custom Software Development, Web Development

Read Also

Node.js: Ryan Dahl's 45-Minute Talk That Rewrote Backend Development (2009)aunimeda
Backend Engineering

Node.js: Ryan Dahl's 45-Minute Talk That Rewrote Backend Development (2009)

On November 8, 2009, Ryan Dahl presented Node.js at JSConf EU in Berlin. He showed JavaScript running on the server with non-blocking I/O. The audience sat in silence, then gave a standing ovation. Within three years, Node.js had more packages than any other programming language ecosystem. This is what he built and why it worked.

Google Chrome and V8: The Day JavaScript Got Fast (2008)aunimeda
Backend Engineering

Google Chrome and V8: The Day JavaScript Got Fast (2008)

On September 2, 2008, Google released Chrome 0.2 with the V8 JavaScript engine. V8 compiled JavaScript directly to native machine code. Benchmarks showed it 10-50x faster than IE7. Within eighteen months, every browser had raced to match it. JavaScript performance went from a constant limitation to a solved problem.

The iPhone Problem: Every Website Broke on June 29, 2007aunimeda
Backend Engineering

The iPhone Problem: Every Website Broke on June 29, 2007

On June 29, 2007, Apple released the iPhone. It ran a real browser - Safari on WebKit - not a WAP-stripped proxy. For the first time, users expected actual websites on mobile. And virtually every website on the internet looked terrible. This is what we did about it.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Get Consultation All articles