# Processing sketch as a web page server

**URL:** <https://discourse.processing.org/t/processing-sketch-as-a-web-page-server/47206>\
**Category:** Gallery\
**Created:** [September 23, 2025, 12:56am UTC](https://discourse.processing.org/t/processing-sketch-as-a-web-page-server/47206 "2025-09-23T00:56:26Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [September 23, 2025, 12:56am UTC](https://discourse.processing.org/t/processing-sketch-as-a-web-page-server/47206/1 "2025-09-23T00:56:27Z")

</div>

Hello folks!

Processing sketch as a web page server:

```processing
// 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);
  }  

```

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/b/9/b987efcfca0eed61c2f88a5d18c6505cff85dc74.png)

Learned something new:

> **[Mastering \`.formatted\` in Java](https://www.javaspring.net/blog/formatted-java/)**
>
> In Java, the \`.formatted\` method is a powerful addition that simplifies the process of creating formatted strings. It provides a more intuitive and concise way to format text compared to traditional methods like \`String.format()\`. With the...

That was fun!

`:)`

---

<div class="post-metadata">

**Author:** ![mcintyre](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/mcintyre/32/14561_2.png) [@mcintyre](https://discourse.processing.org/u/mcintyre)\
**Post date:** [September 24, 2025, 11:04pm UTC](https://discourse.processing.org/t/processing-sketch-as-a-web-page-server/47206/2 "2025-09-24T23:04:25Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![adaptinnovative](https://avatars.discourse-cdn.com/v4/letter/a/2acd7d/32.png) [@adaptinnovative](https://discourse.processing.org/u/adaptinnovative)\
**Post date:** [May 22, 2026, 10:23am UTC](https://discourse.processing.org/t/processing-sketch-as-a-web-page-server/47206/3 "2026-05-22T10:23:51Z")

</div>

This looks awesome! A really good idea.

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [July 10, 2026, 7:07pm UTC](https://discourse.processing.org/t/processing-sketch-as-a-web-page-server/47206/4 "2026-07-10T19:07:34Z")

</div>

Updated version:

> **Code**
>
> ```processing
> 
> // 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:

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/a/e/aea5e13b742160ea5e958de6a6c06d96974f3f4e.png)

`:)`
