articles » current » blazor » wasm » enable-three-js

Blazor: Enable ThreeJS Example

How to enable Three.js to work in Blazor WebAssembly

This example uses .NET 6 but it is not too different for older versions such as .NET 5 and .NET Core 3.1.

It is incredibly easy to enable Three.js for Blazor WASM to access. It requires writing one JavaScript file for the project, adding a touch of code to the index.html and modifying Index.razor.

First, create a Blazor WASM project with all the boilerplate code that it typically comes with.

Create main.js JavaScript File

The function threeExample() must be created. Name this JavaScript file main.js and place it in the wwwroot/js folder.

// main.js
function threeExample(canvasId) { // NOTE: three dancing balls example

	var canvas = document.getElementById(canvasId);

	// NOTE: create the scene to place objects in
	var scene = new THREE.Scene();
	scene.background = new THREE.Color(0x6699cc); // NOTE: make the background blue for the sky
	scene.autoUpdate = true;



	// NOTE: the width and height of the parent element; this information is static and should be updated when the browser window is resized
	var size = {
		width: canvas.parentNode.offsetWidth,
		height: canvas.parentNode.offsetHeight
	};

	// NOTE: issue these statements when resizing the window
	// camera.aspect = size.width / size.height;
	// camera.updateProjectionMatrix();
	// renderer.setPixelRatio(window.devicePixelRatio);
	// renderer.setSize(size.width, size.height);


	
	// NOTE: create the camera with 53 degree field of view; this is how the scene is viewed by the user
	var camera = new THREE.PerspectiveCamera(53, size.width / size.height, 1, 5000);

	// NOTE: position the camera in space a bit
	camera.position.z = 5;


	var renderer = new THREE.WebGLRenderer({
		canvas: canvas
	});
	renderer.shadowMap.enabled = true;
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(size.width, size.height);
	renderer.render(scene, camera);



	// NOTE: create three spheres
	var sphere1 = new THREE.Mesh(new THREE.SphereGeometry(0.75), new THREE.MeshLambertMaterial({ color: 0xff0000 }));
	sphere1.position.x = sphere1.position.y = 0;
	sphere1.position.z = -50;
	sphere1.receiveShadow = true;
	sphere1.castShadow = true;
	scene.add(sphere1);


	var sphere2 = new THREE.Mesh(new THREE.SphereGeometry(0.75), new THREE.MeshLambertMaterial({ color: 0x00ff00 }));
	sphere2.position.x = sphere2.position.y = 0.5;
	sphere2.position.z = -25;
	sphere2.receiveShadow = true;
	sphere2.castShadow = true;
	scene.add(sphere2);


	var sphere3 = new THREE.Mesh(new THREE.SphereGeometry(0.75), new THREE.MeshLambertMaterial({ color: 0x0000ff }));
	sphere3.position.x = sphere3.position.y = 0.5;
	sphere3.position.z = -25;
	sphere3.receiveShadow = true;
	sphere3.castShadow = true;
	scene.add(sphere3);



	// NOTE: create the ground
	var plane = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000), new THREE.MeshLambertMaterial({ color: 0x00aa00 }));
	plane.position.x = 0;
	plane.position.y = 0;
	plane.position.z = -50;
	plane.rotation.x = 4.75;
	plane.receiveShadow = true;
	scene.add(plane);



	// NOTE: this light will follow the mouse cursor
	var light = new THREE.PointLight(0xffffff);
	light.position.x = 51.5;
	light.position.y = 862.5;
	light.position.z = 10;
	light.castShadow = true;
	light.shadow.camera.top = 2500;
	light.shadow.camera.bottom = - 2500;
	light.shadow.camera.left = - 2500;
	light.shadow.camera.right = 2500;
	light.shadow.camera.near = 1;
	light.shadow.camera.far = 1000;
	light.shadow.mapSize.set(2048, 2048);

	scene.add(light);

	// NOTE: this function will set the light based on the mouse cursor
	var moveLight = function (event) {
		var offX = 0;
		var offY = 0;
		if (typeof window.pageXOffset != "undefined") {
			offX = window.pageXOffset;
			offY = window.pageYOffset;
		}
		else {
			if (document.documentElement.scrollTop == 0) {
				offX = document.body.scrollLeft;
				offY = document.body.scrollTop;
			}
			else {
				offX = document.documentElement.scrollLeft;
				offY = document.documentElement.scrollTop;
			}
		}
		var x, y;
		if (typeof event.pageX != "undefined") {
			x = event.pageX;
			y = event.pageY;
		}
		else {
			x = event.clientX;
			y = event.clientY;
		}
		x -= offX;
		y -= offY;
		if (x < 0) {
			x = 0;
		}
		if (y < 0) {
			y = 0;
		}

		light.position.x = x - size.width / 2;
		light.position.y = size.height / 2 - y;
	};

	var handler = function (element, type, func) {
		if (element.addEventListener) {
			element.addEventListener(type, func, false);
		} else if (window.attachEvent) {
			element.attachEvent("on" + type, func);
		} else {
			element["on" + type] = func;
		}
	};

	handler(canvas, "mousemove", moveLight);


	// NOTE: this will reset the light after the mouse moves out
	handler(canvas, "mouseout", function () {
		light.position.x = 51.5;
		light.position.y = 862.5;
		light.position.z = 10;
	});


	// NOTE: MUST HAVE AN ANIMATE FUNCTION
	var animate = function () {
		var time = Date.now();

		sphere1.position.x = Math.cos(time * (1.0 / 200) + 10.0);
		sphere1.position.y = Math.sin(time * (1.0 / (5 * 200)) + 10.0) + 5;
		sphere1.position.z = Math.sin(time * (1.0 / 200) + 10.0) - 10;

		sphere2.position.x = Math.cos(time * (1.0 / (5 * 200)) + 10.0) * 1.2;
		sphere2.position.y = Math.sin(time * (1.0 / 200) + 10.0) * 1.2 + 5;

		sphere3.position.x = Math.cos(time * (1.0 / (5 * 200)) + 10.0) * 1.5 + 6;
		sphere3.position.y = Math.sin(time * (1.0 / (5 * 200)) + 10.0) * 1.5 + 7;

		requestAnimationFrame(animate);
		renderer.render(scene, camera);
	};
	animate();

}

Modify the index.html File

The three.min.js and main.js source files must be linked in the index.html file like this:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Blazor + Three.js</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorThree.Client.styles.css" rel="stylesheet" />
    <!-- NOTE: SOME STYLING ADDED HERE BUT IT MAY BE ADDED TO THE CSS FILE INSTEAD -->
    <style>
        main { 
            min-height: 75vh;
        }
        article {
            height: 100%;
        }
    </style>
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>

    <!-- NOTE: THE FOLLOWING TWO LINES ADDED -->
    <script src="/js/three.min.js"></script>
    <script src="/js/main.js"></script>

</body>

</html>

Dowload the three.min.js file here: THREEJS.zip

Modify a RAZOR Page for Three.js to Use

Modify the Index.razor page to inject the IJSRuntime interface and use it to call the threeExample() JavaScript function. It also adds a canvas element for the Three.js renderer.

@page "/"
@inject IJSRuntime JSRuntime

<PageTitle>Blazor + Three.js</PageTitle><!-- NOTE: THIS LINE IS NOT COMPATIBLE WITH OLDER VERSIONS OF .NET -->

<canvas id="@canvasId"></canvas><!-- NOTE: THIS LINE IS NEW -->

@code {
	const string canvasId = "canvas-id"; // NOTE: the ID of the canvas

	protected override async Task OnAfterRenderAsync(bool firstRender)
	{
		object[] args = { canvasId };
		await JSRuntime.InvokeVoidAsync("threeExample", args); // NOTE: call JavaScript function with the ID of the canvas
	}
}

That's all that's needed to enable Three.js in .NET!


This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE