Launcher for Multi-Renderer PApplets

Hello folks!

My exploration of a launcher for PApplets with different renderers.

This was used primarily for testing Windows scaling on my W10 system and how it affects the sketch window.

Code:

/**
 * Launcher for Multi-Renderer PApplets
 * 
 * Author: glv
 * Date: 2025-07-07
 * 
 * Description:
 * Launches four separate Processing sketches (PApplets) using different renderers:
 * - JAVA2D
 * - P2D
 * - P3D
 * - FX2D
 * 
 * Each renderer runs in its own thread, with readiness flags for communication.
 * Allows visual comparison of renderer behavior and DPI scaling issues on Windows.
 * 
 * Notes:
 * - Uses `volatile` flags to ensure safe thread communication.
 * - Each PApplet has a window title and custom location.
 * - Both lambda and traditional Runnable launch methods shown for `app_0`.
 * - Can be extended for inter-applet communication or benchmarking.
 * 
 * Assisted by: ChatGPT (OpenAI)
 */


// Processing JavaFX library
import processing.javafx.*;

// Renderer Applets:
renderApp_0 app_0; // JAVA2D
renderApp_1 app_1; // P2D
renderApp_2 app_2; // P3D
renderApp_3 app_3; // FX2D

// Shared volatile flags for thread safety
volatile boolean ready_0 = false;
volatile boolean ready_1 = false;
volatile boolean ready_2 = false;
volatile boolean ready_3 = false;

boolean ready_p0 = false;
boolean ready_p1 = false;
boolean ready_p2 = false;
boolean ready_p3 = false;

int ws = 200;
int sp;

boolean db = false;        // Debug true or false

boolean pr = false;

void settings() {
  size(ws, ws);
  sp = 300;
}

void setup() {
  surface.setTitle("Launcher");
  surface.setVisible(true);
  surface.setLocation(0 * sp, 10);

  displayDensity(1); // Use if necessary

  // app_0 Lambda Thread (active)
  app_0 = new renderApp_0();
  Thread thread_0 = new Thread(() -> PApplet.runSketch(new String[]{"renderapp_0"}, app_0));
  thread_0.start();

  // app_0 Traditional Runnable (commented out)
  /*
  app_0 = new renderApp_0();
  Runnable runnable_0 = new Runnable() {
    @Override
    public void run() {
      PApplet.runSketch(new String[]{"renderapp_0"}, app_0);
    }
  };
  Thread thread_0 = new Thread(runnable_0);
  thread_0.start();
  */

  // app_1
  app_1 = new renderApp_1();
  Thread thread_1 = new Thread(() -> PApplet.runSketch(new String[]{"renderApp_1"}, app_1));
  thread_1.start();

  // app_2
  app_2 = new renderApp_2();
  Thread thread_2 = new Thread(() -> PApplet.runSketch(new String[]{"renderApp_2"}, app_2));
  thread_2.start();

  // app_3  This is 1.25x larger on my W10 PC because of windows scaling settings!
  app_3 = new renderApp_3();
  Thread thread_3 = new Thread(() -> PApplet.runSketch(new String[]{"renderApp_3"}, app_3));
  thread_3.start();
}

void draw() {
  background(255);
  translate(width/2, height/2);

  fill(0);
  textSize(24);
  textAlign(CENTER, CENTER);
  text("Launcher JAVA2D", 0, 0);

  checkStatus();
}

void checkStatus() {
  if (ready_0 && !ready_p0) {
    println("0:", frameCount, millis(), ready_0);
    ready_p0 = true;
  }

  if (ready_1 && !ready_p1) {
    println("1:", frameCount, millis(), ready_1);
    ready_p1 = true;
  }

  if (ready_2 && !ready_p2) {
    println("2:", frameCount, millis(), ready_2);
    ready_p2 = true;
  }

  if (ready_3 && !ready_p3) {
    println("3:", frameCount, millis(), ready_3);
    ready_p3 = true;
  }
}

//****************************************************************

// JAVA2D
class renderApp_0 extends PApplet {
  void settings() {
    if (db) delay(1000);
    size(ws, ws, JAVA2D);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R0 JAVA2D");
    //surface.setLocation(1*sp, 10);
    surface.setLocation(1 * sp * 4/5, 10); // Adjust for scaling of 1.25x (5/4)
    println("R0 JAVA2D");

    ready_0 = true;
  }

  void draw() {
    background(255);
    translate(width/2, 2 * height/3);

    fill(0);
    textSize(24);
    textAlign(CENTER, CENTER);
    text("Renderer 0: \r\n JAVA2D", 0, -height/2);

    strokeWeight(2);
    line(-50, 50, 50, -50);
    line(-50, -50, 50, 50);
  }
}

//****************************************************************

// P2D
class renderApp_1 extends PApplet {
  void settings() {
    if (db) delay(2000);
    size(ws, ws, P2D);

    //noSmooth();
    smooth(4);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R1 P2D");
    surface.setLocation(2 * sp, 10);

    println("R1 P2D:");
    println(2 * sp);
    
    //ortho();
  }

  void draw() {
    background(255);
    translate(width/2, 2 * height/3);

    fill(0);
    textSize(24);
    textAlign(CENTER, CENTER);
    text("Renderer 1: \r\n P2D", 0, -height/2);

    strokeWeight(2);
    line(-50, 50, 50, -50);
    line(-50, -50, 50, 50);
    
    ready_1 = true;
  }
}

//****************************************************************

// P3D
class renderApp_2 extends PApplet {
  void settings() {
    if (db) delay(3000);
    size(ws, ws, P3D);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R2 P3D");
    surface.setLocation(3 * sp, 10);

    println("R2 P3D");
    println(3 * sp);

    ready_2 = true;
  }

  void draw() {
    background(255);
    translate(width/2, height/2);
    lights();

    push();
    translate(0, height/ 8);
    lights();
    fill(0, 255, 0);
    rotateY((frameCount % 360) * (TAU/360));
    //noStroke();
    stroke(128+64);
    sphere(50);
    pop();
    
    //hint(DISABLE_DEPTH_TEST);
    //ortho();
    noLights();
    fill(0);
    textSize(24);
    textAlign(CENTER, CENTER);
    text("Renderer 2: \r\n P3D", 0, -height/3);
    //hint(ENABLE_DEPTH_TEST);        
  }
}

//****************************************************************

// FX2D
class renderApp_3 extends PApplet {
  boolean tog;
  
  void settings() {
    if (db) delay(4000);
    size(ws, ws, FX2D);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R3 FX2D");
    //surface.setLocation(4 * sp, 10);
    surface.setLocation(4*sp*4/5, 10); // // Adjust for scaling of 1.25x (5/4)

    println("R3 FX2D:");
    println(4*sp);

    ready_3 = true;
  }

  void draw() {
    background(255);
    translate(width/2, 2 * height/3);

    fill(0);
    textSize(24);
    textAlign(CENTER, CENTER);
    text("Renderer 3: \r\n FX2D", 0, -height/2);

    strokeWeight(2);
    line(-50, 50, 50, -50);
    line(-50, -50, 50, 50);
  }
}

The displayDensity() is 1 for my monitors until I upgrade.

Processing 4.4.4, W10 and Windows scaling of 125%:

Processing 4.3.4, W10 and Windows scaling of 125%:

:)

Hello folks!

Some updates to the code:

Code 2026-04-20 v1.1.0
/**
 * Launcher for Multi-Renderer PApplets
 * 
 * Author: glv
 * Date: 2026-04-20
 * Version: 1.1.0
 *
 * Description:
 * Launches four separate Processing sketches (PApplets) using different renderers:
 * - JAVA2D
 * - P2D
 * - P3D
 * - FX2D
 * 
 * Each renderer runs in its own thread, with readiness flags for communication.
 * Allows visual comparison of renderer behavior and DPI scaling issues on Windows.
 * 
 * Notes:
 * - Uses `volatile` flags to ensure safe thread communication.
 * - Each PApplet has a window title and custom location.
 * - Both lambda and traditional Runnable launch methods shown for `app_0`.
 * - Can be extended for inter-applet communication or benchmarking.
 * - Insets are read and used to set offsets in surface.setLocation()
 * - Added code to force 1:1 pixel rendering with System.setProperty()
 *
 * Assisted by: ChatGPT (OpenAI) for illustrative code examples, editing, 
 * technical review, and drafting support; overall direction, integration, 
 * and final decisions by the author.
 */

import processing.awt.PSurfaceAWT;
import java.awt.Insets;  

volatile int sx = 0;

// Processing JavaFX library
import processing.javafx.*;

// Renderer Applets:
renderApp_0 app_0; // JAVA2D
renderApp_1 app_1; // P2D
renderApp_2 app_2; // P3D
renderApp_3 app_3; // FX2D

// Shared volatile flags for thread safety
volatile boolean ready_0 = false;
volatile boolean ready_1 = false;
volatile boolean ready_2 = false;
volatile boolean ready_3 = false;

boolean ready_p0 = false;
boolean ready_p1 = false;
boolean ready_p2 = false;
boolean ready_p3 = false;

int w = 200;
int h = 150;
int sp;

boolean db = false;        // Debug true or false

//static // Not required with Processing 4.5.3 (not tested on all other versions)
  {
  // JAVA2D: 
  // Forces 1:1 pixel rendering by disabling OS-level DPI scaling
  System.setProperty("sun.java2d.uiScale", "1.0");

  // FX2D: 
  // Forces 1:1 pixel rendering for the JavaFX-based renderer
  System.setProperty("glass.win.uiScale", "1");
  }

int [] getInsets()
  {
  PSurfaceAWT.SmoothCanvas canvas = (PSurfaceAWT.SmoothCanvas) surface.getNative();
  Insets in = canvas.getFrame().getInsets();
  
  println(" JAVA2D Insets L/T/R/B = " + in.left + " / " + in.top + " / " + in.right + " / " + in.bottom);    
  
  return new int [] {in.left, in.top, in.right, in.bottom}; 
  }


void settings() {
  size(w, h);
  sp = h + 50;
  pixelDensity(1);    // Adjust as required. Must be in settings!
  //noSmooth();      // Visual result was less desirable with this.
}


void setup() {
    println();
    
  //getInsets(); // Prints them
  int insetLeft = getInsets()[0];
  sx = -insetLeft; // Adjust surface.setLocation(sx, 0);
  println();
    
  surface.setTitle("Launcher");
  surface.setVisible(true);
  surface.setLocation(sx, 0);    
       
  textSize(20);
  println("displayDensity:", displayDensity());

  // app_0 Lambda Thread
  app_0 = new renderApp_0();
  Thread thread_0 = new Thread(() -> PApplet.runSketch(new String[]{"renderApp_0"}, app_0));
  thread_0.start();

  // app_0 Traditional Runnable (commented out)
  /*
  app_0 = new renderApp_0();
  Runnable runnable_0 = new Runnable() {
    @Override
    public void run() {
      PApplet.runSketch(new String[]{"renderApp_0"}, app_0);
    }
  };
  Thread thread_0 = new Thread(runnable_0);
  thread_0.start();
  */

  // app_1
  app_1 = new renderApp_1();
  Thread thread_1 = new Thread(() -> PApplet.runSketch(new String[]{"renderApp_1"}, app_1));
  thread_1.start();

  // app_2
  app_2 = new renderApp_2();
  Thread thread_2 = new Thread(() -> PApplet.runSketch(new String[]{"renderApp_2"}, app_2));
  thread_2.start();

  // app_3  
  app_3 = new renderApp_3();
  Thread thread_3 = new Thread(() -> PApplet.runSketch(new String[]{"renderApp_3"}, app_3));
  thread_3.start();
}

void draw() {
  background(255);

  fill(0);
  textSize(20);
  textAlign(CENTER, TOP);
  text("Launcher JAVA2D", width/2, 10);

  checkStatus();
}

void checkStatus() {
  if (ready_0 && !ready_p0) {
    println("0:", frameCount, millis(), ready_0);
    println();
    ready_p0 = true;
  }

  if (ready_1 && !ready_p1) {
    println("1:", frameCount, millis(), ready_1);
    println();
    ready_p1 = true;
  }

  if (ready_2 && !ready_p2) {
    println("2:", frameCount, millis(), ready_2);
    println();
    ready_p2 = true;
  }

  if (ready_3 && !ready_p3) {
    println("3:", frameCount, millis(), ready_3);
    println();
    ready_p3 = true;
  }
}

//****************************************************************

// JAVA2D
class renderApp_0 extends PApplet {
  
  void settings() {
    if (db) delay(1000);
    size(w, h, JAVA2D);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R0 JAVA2D");
    surface.setLocation(sx, 1*sp);
    println("R0 JAVA2D");

    ready_0 = true;
  }

  void draw() {
    background(255);
    
    fill(0);
    textSize(20);
    textAlign(CENTER, TOP);
    text("R0: JAVA2D", width/2, 10);    
    
    translate(width/2, height/2+10);
    strokeWeight(2);
    line(-50, 50, 50, -50);
    line(-50, -50, 50, 50);
  }
}

//****************************************************************

// P2D
class renderApp_1 extends PApplet {
  void settings() {
    if (db) delay(2000);
    size(w, h, P2D);

    //noSmooth();
    smooth(4);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R1 P2D");
    surface.setLocation(sx, 2*sp);

    println("R1 P2D:");
    println(2 * sp);
      
    ready_1 = true;  
  }

  void draw() {
    background(255);

    fill(0);
    textSize(20);
    textAlign(CENTER, TOP);
    text("R1: P2D", width/2, 10);
    
    translate(width/2, height/2+10);
    strokeWeight(2);
    line(-50, 50, 50, -50);
    line(-50, -50, 50, 50);
  }
}

//****************************************************************

// P3D
class renderApp_2 extends PApplet {
  void settings() {
    if (db) delay(3000);
    size(w, h, P3D);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R2 P3D");
    surface.setLocation(sx, 3*sp);

    println("R2 P3D");
    println(3 * sp);

    ready_2 = true;
  }

  void draw() {
    background(255);
    
    fill(0);
    textSize(20);
    textAlign(CENTER, TOP);
    text("R2: P3D", width/2, 10);  
    
   push();
    translate(width/2, height/2 +10);

    lights();
    fill(0, 255, 0);
    float div = 360*2;
    rotateY((frameCount % div) * (TAU/div));
    noFill();
    //noStroke();
    stroke(0);
    strokeWeight(1);
    sphereDetail(6);
    sphere(50);
   pop();       
  }
}

//****************************************************************

// FX2D
class renderApp_3 extends PApplet {
  
  void settings() {
    if (db) delay(4000);
    size(w, h, FX2D);
  }

  void setup() {
    surface.setVisible(true);
    surface.setTitle("R3 FX2D");
    surface.setLocation(sx, 4*sp);

    println("R3 FX2D:");
    println(4*sp);

    ready_3 = true;
  }

  void draw() {
    background(255);
    
    fill(0);
    textSize(20);
    textAlign(CENTER, TOP);
    text("R3: FX2D", width/2, 10);
      
    translate(width/2, height/2 +10);
    strokeWeight(2);
    line(-50, 50, 50, -50);
    line(-50, -50, 50, 50);
  }
}

:)

I love this. I can see how this can work with my project as well

however I use a very old version 1.51