# P3D Cube Renderer with JavaFX Controls

**URL:** <https://discourse.processing.org/t/p3d-cube-renderer-with-javafx-controls/46640>\
**Category:** Gallery\
**Created:** [July 1, 2025, 3:59pm UTC](https://discourse.processing.org/t/p3d-cube-renderer-with-javafx-controls/46640 "2025-07-01T15:59:27Z")\
**Posts on this page:** 1\
**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:** [July 1, 2025, 3:59pm UTC](https://discourse.processing.org/t/p3d-cube-renderer-with-javafx-controls/46640/1 "2025-07-01T15:59:27Z")

</div>

Hello folks!

I was having some fun with _Processing_ and _JavaFX_ integration.

> **Note for new Processing users \< Click here to expand!**
>
> This sketch uses **JavaFX** and advanced Java features to extend Processing beyond its core functionality. Processing is [“only commited to JavaFX as a renderer”](https://github.com/processing/processing4-javafx/issues/15) (FX2D), but it can also be leveraged to create a wide range of features beyond rendering including separate windows, user interfaces, and much more. The integration of JavaFX in Processing is an area that is not extensively documented and still evolving, and much of the information comes from community contributions and experimentation. If you’re interested in exploring this further be prepared for a bit of trial and error, as the integration is not fully documented. Enjoy the ride!
> 
> Using this sketch requires installing the JavaFX library via Processing’s Contribution Manager, importing it, and manually adding JavaFX JAR files into your sketch’s _code_ folder on Windows. There are related topics to assist with this for Windows.
> 
> This example is designed for users with a strong foundation in programming who want to explore how Processing can be extended with external libraries such as JavaFX. If you’re new to Processing, focus first on building solid basics before diving into advanced integrations like this.

This is part of development for some future projects and still in it’s early stages!

> **Code \< Click here to expand!**
>
> ```auto
> /**
> * Background P3D Cube Renderer with JavaFX Controls
> * Author: glv (primary developer and prompt engineer)
> * Assisted by: ChatGPT
> * Date: 2025-07-01
> * Version 1.0.0
> *
> * Description:
> * This Processing sketch demonstrates running a 3D rotating cube
> * on an offscreen PGraphics (P3D) inside a background PApplet,
> * while displaying the live rendered image in a JavaFX ImageView.
> * JavaFX sliders control the cube's yaw (Y axis), pitch (X axis),
> * and roll (Z axis) rotations in real-time.
> *
> * JavaFX UI runs on the JavaFX Application Thread via Platform.runLater(),
> * ensuring toolkit initialization and thread safety.
> * The cube renderer runs independently on a Processing thread.
> *
> * Environment:
> * Processing 4.4.4
> * Windows 10
> * JavaFX 4.0 beta 5 library
> *
> * Note:
> * For W10 and Processing 4.4.4 the JavaFX JAR files
> * need to be placed in the sketch’s 'code' folder
> * to enable JavaFX functionality.
> */
> 
> // Processing JavaFX integration to bridge Processing sketches with JavaFX
> import processing.javafx.*;
> 
> // JavaFX application toolkit for thread management and UI updates
> import javafx.application.Platform;
> 
> // JavaFX classes for scene management and UI components
> import javafx.scene.Scene;
> import javafx.scene.image.*; // ImageView, WritableImage, PixelWriter, PixelFormat
> import javafx.scene.control.*; // Slider, Button, Label
> import javafx.scene.layout.*; // VBox layout container
> import javafx.stage.Stage; // Top-level JavaFX window
> import javafx.geometry.Pos; // Layout alignment constants
> 
> CubeRendererApp cubeApp;
> ImageView imageView;
> Slider yawSlider, pitchSlider, rollSlider;
> Label yawLabel, pitchLabel, rollLabel;
> Button startButton, resetButton;
> 
> volatile PImage flag;
> 
> void settings() {
> size(800, 600);
> }
> 
> void setup() {
> System.setProperty("glass.win.uiScale", "1"); // This works!
>    
> // flag = loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Flag_of_Canada_%28Pantone%29.svg/500px-Flag_of_Canada_%28Pantone%29.svg.png");
> 
> surface.setVisible(false);
> 
> // Force JavaFX toolkit initialization once:
> try {
> Platform.startup(() -> {
> }
> );
> }
> catch (IllegalStateException e) {
> // toolkit already initialized, ignore
> }
> 
> // Now safely run JavaFX UI creation
> Platform.runLater(() -> {
> imageView = new ImageView();
> imageView.setFitWidth(300);
> imageView.setFitHeight(300);
> imageView.setPreserveRatio(true);
> 
> // SLIDERS
> yawSlider = new Slider(-180, 180, 0);
> yawSlider.setShowTickMarks(true);
> yawSlider.setShowTickLabels(true);
> yawSlider.setMajorTickUnit(90);
> yawSlider.setMinorTickCount(2);
> yawSlider.setBlockIncrement(5);
> yawSlider.setSnapToTicks(false);
> yawSlider.setPadding(new javafx.geometry.Insets(0, 10, 0, 10));
> 
> pitchSlider = new Slider(-180, 180, 0);
> pitchSlider.setShowTickMarks(true);
> pitchSlider.setShowTickLabels(true);
> pitchSlider.setMajorTickUnit(90);
> pitchSlider.setMinorTickCount(2);
> pitchSlider.setBlockIncrement(5);
> pitchSlider.setSnapToTicks(false);
> pitchSlider.setPadding(new javafx.geometry.Insets(0, 10, 0, 10));
> 
> rollSlider = new Slider(-180, 180, 0);
> rollSlider.setShowTickMarks(true);
> rollSlider.setShowTickLabels(true);
> rollSlider.setMajorTickUnit(90);
> rollSlider.setMinorTickCount(2);
> rollSlider.setBlockIncrement(5);
> rollSlider.setSnapToTicks(false);
> rollSlider.setPadding(new javafx.geometry.Insets(0, 10, 0, 10));
> 
> // Spacer
> Region spacer = new Region();
> spacer.setMinHeight(20); // or whatever height you want (e.g., 10–50 px)
> 
> // LABELS
> yawLabel = new Label("Yaw (Y axis): 0°");
> pitchLabel = new Label("Pitch (X axis): 0°");
> rollLabel = new Label("Roll (Z axis): 0°");
> 
> // BUTTONS
> startButton = new Button("Start Renderer");
> resetButton = new Button("Reset Rotations");
> 
> startButton.setOnAction(e -> {
> if (cubeApp == null) {
> cubeApp = new CubeRendererApp();
> PApplet.runSketch(new String[]{"CubeRendererApp"}, cubeApp);
> bindSliders();
> }
> }
> );
> 
> resetButton.setOnAction(e -> {
> if (cubeApp != null) {
> yawSlider.setValue(0);
> pitchSlider.setValue(0);
> rollSlider.setValue(0);
> }
> }
> );
> 
> // LAYOUT (only declared once)
> VBox root = new VBox(10,
> imageView,
> spacer,   
> yawLabel, yawSlider,
> pitchLabel, pitchSlider,
> rollLabel, rollSlider,
> startButton,
> resetButton
> );
> root.setAlignment(Pos.CENTER);
> 
> Stage stage = new Stage();
> stage.setScene(new Scene(root, 400, 730));
> stage.setTitle("Background P3D Cube with JavaFX Controls");
> stage.show();
> }
> );
> }
> 
> void draw() {
> background(30);
> fill(255);
> textSize(18);
> text("Main sketch", 20, 40);
> 
> // Update JavaFX ImageView with the latest cube frame
> if (cubeApp != null && cubeApp.sharedCopy != null) {
> WritableImage fxImg = pImageToWritableImage(cubeApp.sharedCopy);
> Platform.runLater(() -> imageView.setImage(fxImg));
> }
> }
> 
> // Bind JavaFX sliders to update cube rotation angles
> void bindSliders() {
> yawSlider.valueProperty().addListener((obs, oldV, newV) -> {
> float v = newV.floatValue();
> yawLabel.setText(String.format("Yaw (Y axis): %.2f", v));
> cubeApp.setYaw(v);
> }
> );
> pitchSlider.valueProperty().addListener((obs, oldV, newV) -> {
> float v = newV.floatValue();
> pitchLabel.setText(String.format("Pitch (X axis): %.2f", v));
> cubeApp.setPitch(v);
> }
> );
> rollSlider.valueProperty().addListener((obs, oldV, newV) -> {
> float v = newV.floatValue();
> rollLabel.setText(String.format("Roll (Z axis): %.2f", v));
> cubeApp.setRoll(v);
> }
> );
> }
> 
> // Convert Processing PImage to JavaFX WritableImage for display
> WritableImage pImageToWritableImage(PImage pImg) {
> pImg.loadPixels();
> WritableImage wimg = new WritableImage(pImg.width, pImg.height);
> PixelWriter pw = wimg.getPixelWriter();
> pw.setPixels(0, 0, pImg.width, pImg.height,
> javafx.scene.image.PixelFormat.getIntArgbInstance(),
> pImg.pixels, 0, pImg.width);
> return wimg;
> }
> 
> // Background Processing PApplet rendering a rotating 3D cube offscreen
> public static class CubeRendererApp extends PApplet {
> PGraphics pg;
> public volatile PImage sharedCopy;
> 
> float yaw=0, pitch=0, roll=0;
>   
> PImage flag;
> 
> public void settings() {
> size(300, 300, P3D);
> }
> 
> public void setup() {
> pg = createGraphics(300, 300, P3D);
> 
> //surface.setVisible(false); Must be visible so will hide it!
> surface.setLocation(-500, -500);
>     
> flag = loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Flag_of_Canada_%28Pantone%29.svg/500px-Flag_of_Canada_%28Pantone%29.svg.png");
> flag.resize(80, 180); 
> flag.save("test.png"); 
> }
> 
> public void draw() {
>     
> // // Position quad exactly on top face
> float w = 40; // half width
> float d = 90; // half depth
> float y = -10.1; // top face y coordinate (half height, negative because up)
>    
> pg.beginDraw();
> pg.background(10);
> pg.lights();
> pg.translate(pg.width/2f, pg.height/2f, 0);
> pg.rotateY(radians(yaw));
> pg.rotateX(radians(pitch));
> pg.rotateZ(radians(roll));
>     
> // Draw textured top face first
> pg.beginShape(QUADS);
> pg.texture(flag);
> 
> // Rotate UVs -90° (counter-clockwise)
> pg.vertex(-w, y, -d, flag.width, 0); // top-left → top-right
> pg.vertex( w, y, -d, flag.width, flag.height); // top-right → bottom-right
> pg.vertex( w, y, d, 0, flag.height); // bottom-right → bottom-left
> pg.vertex(-w, y, d, 0, 0); // bottom-left → top-left
>     
> pg.endShape();
>     
> // Then draw cube box
> pg.noFill();
> pg.stroke(255);
> pg.box(2*40, 2*10, 2*90);
>     
> pg.endDraw();
> 
> // Update shared frame for main sketch display
> sharedCopy = pg.get();
> }
> 
> public void setYaw(float y) {
> yaw = y;
> }
> public void setPitch(float p) {
> pitch = p;
> }
> public void setRoll(float r) {
> roll = r;
> }
> }
> 
> ```

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

`:)`
