How to build a simple "Libre" 3D .glb / .gltf viewer?

Hi guys, how do you do? :slightly_smiling_face:

I modeled a .glb file of an airplane :small_airplane: , it’s attached to this message (demoi12.glb).
I would like to insert it inside a Three.JS glb/gltf viewer, such as this one:

https://threejs.org/examples/webgl_loader_gltf.html

But much simpler! :sweat_smile: (I mentioned this example because the interactive mechanic is similar to what I’m trying to do).

It would be great if the user decided to save the .html webpage (Save page as… —> Web Page, complete) and the .glb file would be saved along with the other files (.html, .css) and the page would work exactly the same way locally and offline for this person.
The idea is to help people understand how Three.js works by providing direct access to all files concerning the viewer.

I’m trying to achieve this goal with the example files from threejs.org website, but they are too complex for my level of understanding, I still can’t make them run locally by simply changing the address of the .gltf file for example.

Any hints are appreciated. :heart:

Thank you very much for your time.

Greetings,

Ortiz

demoi12.glb (3.9 MB)

EDIT: After a lot of research I guess it is kinda of impossible to do that because of cors or without running a local server. I guess I will start with a Wavefront .obj file viewer, since I can embed it at the .html

You could use f3d ? Much simpler than three.js: https://www.npmjs.com/package/f3d

1 Like

Thank you very much for the insight @Mathieu_Westphal!

I will take a look.

I think it should be possible by embedding the glb as a data url, I’ll try tomorrow

1 Like

Ortiz, thanks for your question. Could you let me know your folder sturcture? Thanks.

1 Like

I have put all my files, models, textures, etc. into a GitHub repo. That allows people to run the programs and to view and download the program files and any supporting resource files.

In the programs, all the addresses to supporting modules and files are absolute addresses, not relative. So, if you want to download and run one of my programs locally, you can easily do so.

RUNNING THREE.JS EXAMPLES LOCALLY

You can also run three.js examples locally if you download the program change the import map in the downloaded program. This should work with WebGL examples:

<script type="importmap">
	{
		"imports": {
			"three": "https://unpkg.com/three@0.182.0/build/three.module.js", 
			"three/addons/": "https://unpkg.com/three@0.182.0/examples/jsm/"
		}
	}
</script>

where the number represents the version of three.js.

This should work with WebGPU examples:

<script type="importmap">
	{
		"imports": {
			"three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.webgpu.js",
			"three/webgpu": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.webgpu.js",
			"three/tsl": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.tsl.js",
			"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/"
		}
	}
</script>

If the program contains references to three.js resources, e.g. a model, you need to change any relative addresses to absolute addresses. You can do the same for any styles file or you can download and reference the file locally.

1 Like

You’re basically right. Loading .glb/.gltf from a saved HTML file doesn’t reliably work without running a local server, so it’s not very practical for a fully offline setup.

If the goal is to let people instantly explore a GLB project without setup, you can use this viewer instead:
https://theneoverse.web.app/#threeviewer&&construct

Drag & drop icon (button):

image

drag and drop your glb in here:

It allows loading and exploring .glb files directly in a Three.js based virtual experience.

1 Like

So I’ve tried with a dataurl and it works fine.

The code

const response = await fetch(url);

const blob = await response.blob();

const dataUrl = await new Promise<string>((resolve, reject) => {

  const reader = new FileReader();

  reader.onloadend = () => resolve(reader.result as string);

  reader.onerror = reject;

  reader.readAsDataURL(blob);

});




const gltf = await loader.loadAsync(dataUrl);

You can totally have the dataurl and everything you need in a single html page without any other files, the css and the script can be in it. However if you want to support draco, meshopt, basis etc you’ll need more than just that.

Otherwise you can host your models somewhere but well it’s online but maybe less odd. For sample here is your model in my viewer Super GLB Viewer: Inspect, Edit & Compare 3D Models with Three.js, Babylon.js & PlayCanvas , your model is in the query url ?models=https://assets.jessyleite.dev/models/chicortiz.glb

The setup is not really complex, you can just setup a R2 bucket on cloudflare and vibe code a form so you can upload your model, then you just need to manage the cors and voilà, you can import it in your own viewer. Should take like 20 min.

1 Like

Hi guys ( @jessyleite , @Hotdev , @ phill_crowther , @ Umbawa_Sarosong ), thank you very much for the incredible resources!

Lots of things to study here. I hope to finish a demo file son. :sweat_smile:

1 Like

Hi @Hotdev , I’m trying to put everything inside a .html, maybe something inside a .css file, very simple stuff :wink: Thanks for asking.

You can do this but you’d have to embed the GLTF as base64 in the html.

1 Like

Hi guys this is the best I got so far: 3D Model Viewer - Demoiselle

Using Wavefront .obj and .mtl inside the code for the moment, which I’m more familiar with. Since the model has to many faces I’m attaching the base file to this message which is just a cube placeholder for ctrl c + ctrl v.

I used Deepseek to write the .html, I hope you don’t get sad because of that, overall I don’t like A.I. the result is very dirty and unmantainable. the thing is my background is on the Blender Game Engine, now UPBGE which is a complete different beast, so my skills with WebDev really suck.

At least I was able to load everything offline, but once I upload it to the web service it changes the call to the .js dependencies, and even if I download it and change it manually it doesn’t work (can’t find the “three.min.js”) even though it’s there. Well, it’s what happens when you trust a sloppy agent.

Next step is to explore the .gltf options to export as base64 in Blender like @manthrax suggested. I guess this will make it easier to incorporate animations latter on.

Q_base.html (24.9 KB)

When you get around to animating things:

Here is a discussion on how to import airplane (and other) animations from Blender to three.js.

And here is a program that creates an airplane with animated parts solely within three.js - both external controls surfaces and internal gauges. You might be able to use this approach to add animated parts, like the control surfaces, to your model within three.js.

EDIT: I just remembered that the Demoiselle airplane does not have ailerons, but uses wing warping. Animating that in Blender could be a real challenge! For me, that would not be a huge challenge because I have WebGL and WebGPU shader programs that can alter the surface of objects, like a waving flag or ocean waves. But that is all probably way down the road in your process. I just wanted to let you know that it can be done.

2 Likes

Awesome stuff @phil_crowther , thank you so much! Your flight simulator is impressive. (I had lots of fun until stabilizing the airplane with the mouse).

For the moment I’m trying some basic stuff, but it’s very inspiring seeing what one can achieve with Three.JS!

1 Like

Hi guys!

Inspired by the input I received here, I was able to advance a little bit more.

Now there is this base where one can copy and past the Wavefront .obj file and corresponding .mtl file:

AAA_P_base.html (35.9 KB)

In the above file there is transparent cube as dummy. So it’s just copy and paste in this section:

Here is the result with the Demoiselle model (exact same file, .obj and .mtl pasted into the correspondent section) This model was generated in Blender version 5.0.1, using the default options of the Wavefront .obj exporter (It’s a monstrosity since the number of faces is enormous) :

Now I will try to replace the Wavefront .obj and .mtl section with the base64 option of .gltf. More info about how to generate this kinda of file in Blender can be found here:

It’s kinda of hidden in Blender, so I copied the pictures of the stackexchange here for quick reference:

And also the Warning message by the reply autor, (@ emackey) :

Caution: The base64 “Embedded” format is so inefficient compared to the “Separate” (.bin) and “Binary” (.glb) formats that Khronos has opted to hide the “Embedded” option behind a little-used flag in the glTF add-on settings panel.

Be certain you understand the inefficiency of base64 and have a valid reason to actually use this format before enabling this option! If you still want to use it, turn it on in the add-on settings panel here:

Once enabled, the “Embedded” format becomes available from the glTF export format drop-down chooser.

Blender offers a “Format” chooser at the top of the export options for glTF, with three options:

  • glTF Separate (.gltf + .bin + textures)

  • glTF Embedded (.gltf)

  • glTF Binary (.glb)

The last one, *.glb, is the default. This is the most compact and shareable form of glTF, where all assets are packed into one binary file. It does not use Base64 encoding, all of the binary assets are true binary here.

The top one, “glTF Separate”, is great for debugging or further hand-edits to the exported file. It’s perhaps not as good for sharing, due to the use of multiple files. The overall node hierarchy and settings are in plain text (JSON) in the .gltf file, which references a .bin that contains mesh data, and also references .jpg and .png image files for texturemaps.

The middle one, “glTF Embedded”, is the only one that does Base64 encoding. It packs the glTF model, all binary mesh data, and all PNG/JPG images, all into a single .gltf plain text JSON file, using Base64 to encode the binary parts to plain text. This is the least efficient form, and I don’t recommend it for normal use. But, this appears to be the one you’re asking about in the original question, so if you really need this one, you can just choose it from the combo box shown here. The file will be a fair percentage larger than the other two forms, due to the Base64 encoding.

2 Likes