Solution 1. Text3D
There is no built-in JavaFX 3D Text node. However you can use a Text3DMesh node from the FXyz library.
Something like this:
build.gradle
plugins { id 'application' id 'org.openjfx.javafxplugin' version '0.0.7' } repositories { jcenter() mavenCentral() } dependencies { implementation 'org.fxyz3d:fxyz3d:0.4.0' } javafx { version = "12" modules = [ 'javafx.controls' ] } mainClassName = 'text3d.Text3D'
text3d.Text3D.java
public class Text3D extends Application { private final Rotate rotateX = new Rotate(10, Rotate.X_AXIS); private final Rotate rotateY = new Rotate(-10, Rotate.Y_AXIS); private double mousePosX, mousePosY, mouseOldX, mouseOldY; @Override public void start(Stage stage) throws Exception { PerspectiveCamera camera = new PerspectiveCamera(true); camera.setNearClip(0.1); camera.setFarClip(10000.0); camera.getTransforms().addAll (rotateX, rotateY, new Translate(-100, 100, -1000)); Text3DMesh text3D = new Text3DMesh("Text3D"); text3D.setFont(Font.getFamilies().get(new Random().nextInt(Font.getFamilies().size()))); text3D.setFontSize(200); text3D.setTextureModeVertices3D(1530, p -> p.y); Bounds bounds = text3D.getBoundsInParent(); text3D.setTranslateX(- bounds.getWidth() / 2d); text3D.setTranslateY(bounds.getHeight() / 2d); text3D.setTranslateZ(-bounds.getDepth()); double radius = Math.max(bounds.getWidth(), bounds.getHeight()) * 1.2; SpheroidMesh spheroid = new SpheroidMesh(radius); spheroid.setTextureModeVertices3D(1530, p -> p.x * p.y); spheroid.setTranslateZ(radius); Group group = new Group(spheroid, text3D); Scene scene = new Scene(group, 400, 300, true, SceneAntialiasing.BALANCED); scene.setFill(Color.BISQUE); scene.setCamera(camera); scene.setOnMousePressed(event -> { mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); }); scene.setOnMouseDragged(event -> { mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY)); rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX)); mouseOldX = mousePosX; mouseOldY = mousePosY; }); stage.setScene(scene); stage.setTitle("FXyz3D Sample"); stage.show(); } }

Solution 2. Image
Another approach can be done by adding an image as diffuse map of the sphere. Again, this can be done with FXyz.
First, you need to render a regular Text 2D node on a scene, and do a snapshot of it to generate the image.
text3d.Image3D
public class Image3D extends Application { private final Rotate rotateX = new Rotate(10, Rotate.X_AXIS); private final Rotate rotateY = new Rotate(-10, Rotate.Y_AXIS); private double mousePosX; private double mousePosY; private double mouseOldX; private double mouseOldY; @Override public void start(Stage stage) throws Exception { PerspectiveCamera camera = new PerspectiveCamera(true); camera.setNearClip(0.1); camera.setFarClip(10000.0); camera.setFieldOfView(20); camera.getTransforms().addAll (rotateX, rotateY, new Translate(-100, 100, -2000)); Text text = new Text(" Text3D "); text.setStroke(Color.DARKGOLDENROD); text.setFill(Color.DARKGOLDENROD); text.setFont(Font.font(Font.getFamilies().get(new Random().nextInt(Font.getFamilies().size())), 30)); Group root = new Group(text); Scene sceneAux = new Scene(root, root.getBoundsInLocal().getWidth(), root.getBoundsInLocal().getHeight()); SnapshotParameters sp = new SnapshotParameters(); double s = Screen.getPrimary().getOutputScaleX(); sp.setTransform(new Scale(s, s)); sp.setFill(Color.DARKMAGENTA); Image image = root.snapshot(sp, null); double radius = 400; SpheroidMesh spheroid = new SpheroidMesh(radius); ((PhongMaterial) spheroid.getMaterial()).setDiffuseMap(image); Group group = new Group(spheroid); Scene scene = new Scene(group, 400, 300, true, SceneAntialiasing.BALANCED); scene.setFill(Color.BISQUE); scene.setCamera(camera); scene.setOnMousePressed(event -> { mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); }); scene.setOnMouseDragged(event -> { mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY)); rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX)); mouseOldX = mousePosX; mouseOldY = mousePosY; }); stage.setScene(scene); stage.setTitle("FXyz3D Sample"); stage.show(); } }

EDIT
Solution 3
While this is not part of FXyz, you can also apply some transforms to wrap the Text3DMesh around the sphere.
Each letter basically is a TriangleMesh with a translate transform in X (offset).
So we take all the points for each letter, remove the transform, and apply the offset, so we have absolute coordinates:
x = x + offset;
Then we calculate the bending angle based on a given arc length we want and the actual width w of the text node.
t = (x - w/2) / (w/2) * Pi/2;
Then, for each point, its new coordinates will be:
x = (R + z) * cos(t); z = (R + z) * Sin(t);
text3d.CurvedText3D.java
public class CurvedText3D extends Application { private final Rotate rotateX = new Rotate(10, Rotate.X_AXIS); private final Rotate rotateY = new Rotate(-10, Rotate.Y_AXIS); private double mousePosX, mousePosY, mouseOldX, mouseOldY; @Override public void start(Stage stage) throws Exception { PerspectiveCamera camera = new PerspectiveCamera(true); camera.setNearClip(0.1); camera.setFarClip(10000.0); camera.setFieldOfView(40); camera.getTransforms().addAll (rotateX, rotateY, new Translate(-100, 100, -2000)); Text3DMesh text3D = new Text3DMesh("Round Text3D", Font.getFamilies().get(new Random().nextInt(Font.getFamilies().size())), 100, true); text3D.setHeight(30); text3D.setTextureModeNone(Color.CRIMSON); Bounds bounds = text3D.getBoundsInParent(); double width = bounds.getWidth(); double radius = Math.max(width, bounds.getHeight()); text3D.getMeshes().stream() .forEach(m -> { ObservableFloatArray points = ((TriangleMesh) m.getMesh()).getPoints(); float[] v = new float[points.size()]; points.toArray(v); double offset = m.getTransforms().get(0).getTx(); m.getTransforms().clear(); for (int i = 0; i < v.length; i += 3) { v[i] += offset; double t0 = (v[i] - width / 2d) / (width / 2d) * Math.PI / 2; double t1 = (radius + v[i + 2]) * Math.cos(t0); double t2 = (radius + v[i + 2]) * Math.sin(t0); v[i] = (float) t1; v[i + 2] = (float) t2; } ((TriangleMesh) m.getMesh()).getPoints().setAll(v); }); SpheroidMesh spheroid = new SpheroidMesh(radius * 0.9); spheroid.setTextureModeVertices3D(1530, p -> p.x * p.y); Group group = new Group(spheroid, text3D); Scene scene = new Scene(group, 400, 300, true, SceneAntialiasing.BALANCED); scene.setFill(Color.BISQUE); scene.setCamera(camera); scene.setOnMousePressed(event -> { mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); }); scene.setOnMouseDragged(event -> { mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY)); rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX)); mouseOldX = mousePosX; mouseOldY = mousePosY; }); stage.setScene(scene); stage.setTitle("FXyz3D Sample"); stage.show(); } }
