Processing sketch as a web page server

Hello folks!

Processing sketch as a web page server:

// Processing sketch as a web page server
// Author: glv
// Date:   2025-09-22
// Insight gleaned from various sources; seek and you shall find!

// You will see this in code:
// https://github.com/processing/processing/issues/3937

// Sketch will launch this:
// Wep Page server at http://127.0.0.1:20000/1

import processing.net.*;

Server s;
Client c;

String incoming;

String time;
int count = 0;

void setup()
  {
  size(200, 200, P2D);
  
  // Starts a myServer on port 20000
  s = new Server(this, 20000);

  link("http://127.0.0.1:20000/1"); // Comment this if it does now work and lauhc browser manually!
  delay(500);
  }

void draw()
  {
  background(255);
  fill(0);
  
  // Reference:
  //<h3>Count:</h3>
  //<h3>%d</h3>
  //<h3>Time:</h3>
  //<h3>%s</h3>
  
  textSize(24);
  String txt = "Count:\r\n" + count + 
               "\r\n\r\n" + 
               "Time:\r\n" + time; 
  
  text(txt, 50, 50);
  }

void clientEvent(Client c) 
    { 
    incoming = c.readString();
    println("Received:\r\n"+ incoming);
    
    time = nf(hour(),2) + ":" + nf(minute(),2) + ":" + nf(second(), 2);
    println(time);
    
    incoming = incoming.substring(0, incoming.indexOf("\r\n")); // Only up to the newline
    if (incoming.equals("GET /1 HTTP/1.1"))
      {
      webPage(time, count);     
      count++;
      println("Count sent: " + count);
      println();
      println("*************************************************************");
      println();
      redraw();
      }
  }

void webPage(String _time, int _count) 
  {    
  
  String htmlContent = """
        <!DOCTYPE html>
        <html>
            <head>
                <meta http-equiv="refresh" content="1">
                <link rel="icon" href="data:,">
                <title>Live Counter</title>
            </head>
            <body>
                <h3>Count:</h3>
                <h3>%d</h3>
                <h3>Time:</h3>
                <h3>%s</h3>
            </body>
        </html>
        """.stripIndent().formatted(_count, _time);

String httpResponse = """
        HTTP/1.1 200 OK
        Content-Length: %d
        Content-Type: text/html
        Connection: close
        
        %s
        """.stripIndent().formatted(htmlContent.length(), htmlContent);

  // Send the complete HTTP response
  s.write(httpResponse.getBytes());
  // println(httpResponse);
  }  


Learned something new:

That was fun!

:)

This is great @glv – thanks for sharing! Just started making networked sketches with my AP CSP class and will try this out with them soon.

This looks awesome! A really good idea.

Updated version:

Code

// Minimal Local HTTP Server Demo in Processing
// Author: glv
// Version: 1.1.5
// Original: 2025-09-22
// Updated:  2026-07-10 15:00 EDT
//
// Minimal local HTTP response demo using Processing's Network library.
// Opened in the browser at: http://127.0.0.1:20000/1
//
// In this version:
// - The response is sent through the Client that made the request.
// - The server listens on the local loopback interface only.
// - HTTP headers use CRLF line endings and a UTF-8 body length.
//

// Future improvement: Add explicit HTTP error responses for unsupported or malformed requests.

// https://en.wikipedia.org/wiki/ASCII_art
// Original ASCII artwork: Axel Liljencrantz
// Source: Wikimedia Commons, via Wikipedia’s “ASCII art” article
// License: GNU General Public License v2.0

import java.nio.charset.StandardCharsets;
import processing.net.*;

Server s;

String time = "";
int count = 0;

boolean singleWriteResponse = false;

void setup()
  {
  size(200, 200);

  s = new Server(this, 20000, "127.0.0.1");
  link("http://127.0.0.1:20000/1");

  noLoop();
  }

void draw()
  {
  background(255);
  fill(0);
  textSize(24);

  String txt = "Count:\r\n" + count +
               "\r\n\r\n" +
               "Time:\r\n" + time;

  text(txt, 50, 50);
  }

void clientEvent(Client c)
  {
  // Simple local demo that assumes the browser request line arrives in one read.
  // A production HTTP server would buffer input until the full request arrives.
  String incoming = c.readString();

  if (incoming == null)
    {
    c.stop();
    return;
    }

  println("Received:\r\n" + incoming);

  int lineEnd = incoming.indexOf("\r\n");
  if (lineEnd == -1)
    {
    c.stop();
    return;
    }

  String requestLine = incoming.substring(0, lineEnd);

  if (requestLine.equals("GET /1 HTTP/1.1"))
    {
    // Route the selected page template to the shared webpage function.
    webPage(c, htmlContent1);
    }
  else
    {
    c.stop();
    }
  }

// Update the displayed values, format the selected page template, and send it.
void webPage(Client c, String pageContent)
  {
  time = nf(hour(), 2) + ":" + nf(minute(), 2) + ":" + nf(second(), 2);
  count++;

  response(c, pageContent.formatted(count, time));

  c.stop();
  println("Count sent: " + count);
  redraw();
  }
  

void response(Client c, String htmlContent)
  {     
  byte[] htmlBytes = htmlContent.getBytes(StandardCharsets.UTF_8);   
  
  // The singleWriteResponse boolean selects one of these response methods.

  // Option 1: build and write the complete response in one call.
  if(singleWriteResponse)
    {
    String httpResponse = (
        "HTTP/1.1 200 OK\r\n" +
        "Content-Length: %d\r\n" +
        "Content-Type: text/html; charset=UTF-8\r\n" +
        "Connection: close\r\n" +
        "\r\n" +
        "%s"
        ).formatted(htmlBytes.length, htmlContent);
  
    c.write(httpResponse.getBytes(StandardCharsets.UTF_8));
    }

  // Option 2: write ASCII headers and the UTF-8 body separately.
  else
    {  
    String httpHeaders =
        "HTTP/1.1 200 OK\r\n" +
        "Content-Length: " + htmlBytes.length + "\r\n" +
        "Content-Type: text/html; charset=UTF-8\r\n" +
        "Connection: close\r\n" +
        "\r\n";
  
    c.write(httpHeaders.getBytes(StandardCharsets.US_ASCII));
    c.write(htmlBytes);
    }      
  }  
  
  
// Original example.
String htmlContent0 = """
        <!DOCTYPE html>
        <html>
            <head>
                <meta http-equiv="refresh" content="1">
                <link rel="icon" href="data:,">
                <title>Live Counter</title>
            </head>
            <body>
                <h3>Count:</h3>
                <h3>%d</h3>
                <h3>Time:</h3>
                
                <h3>%s</h3>
            </body>
        </html>
        """
        .stripIndent();
        //.formatted(count, time);  // Formatting is performed in webPage().
        

// Display the ASCII characters from the classic fish-shell fish.
String htmlContent1 = """
        <!DOCTYPE html>
        <html>
            <head>
                <meta http-equiv="refresh" content="1">
                <link rel="icon" href="data:,">
                <title>ASCII Fish</title>
            </head>
            <body>
                <pre style="font-size:20px;">
                ___
  ___======____=---=)
/T            \\_--===)
[ \\ (O)   \\~    \\_-==)
 \\      / )J~~    \\-=)
  \\\\___/  )JJ~~~   \\)
   \\_____/JJJ~~~~    \\
   / \\  , \\J~~~~~     \\
  (-\\)\\=|\\\\\\~~~~       L__
  (\\\\)  (\\\\\\)_           \\==__
   \\V    \\\\\\) ===_____   \\\\\\\\\\\\
          \\V)     \\_) \\\\\\\\JJ\\J\\)
                      /J\\JT\\JJJJ)
                      (JJJ| \\UUU)
                       (UU)
                </pre>
                <p>Count: %d &nbsp; Time: %s</p>
                <p><small>ASCII fish artwork by Axel Liljencrantz. Source: Wikimedia Commons. Licensed under GNU GPL v2.</small></p>
            </body>
        </html>
        """
        .stripIndent();
        //.formatted(count, time);       
  

Web page:

:)