Hi!
Besides using the in-editor “Build and Run” feature, how can I run a WebGPU build locally?
I’m wondering especially since WebGPU is only available in secured contexts (i. e. HTTPS).
Do I need to set up server configuration for this, or is there a simpler way of doing it?
You can use localhost to run webgpu content locally, so any local web server, like python -m http-server or nodejs, will work.
Thank you for the quick response!
Good to know.
One caveat I noticed is that python -m http-server must be ran after some extra steps when doing testing situations where Code Optimization is enabled in the build settings: Disk Size LTO & Disk Size --“Build.framework.js” becomes “Build.framework.js.br” etc.
Then this error occurs when loading the page:
Unable to load file Build/Build.framework.js! Check that the file exists on the remote server. (also check browser Console and Devtools Network tab to debug)
So a custom python script that uncompresses brotli files seems to be the solution for testing release builds
Would have been nice, and most likely would have saved me and others for time if this info about running local webgl and webgpu builds could be added to the Web documentation. For instance in: Unity - Manual: Web development and publishing process or Unity - Manual: Server configuration code samples
if you’re sticking with brotti…
ive hacked this together with a bit of help
import http.server
import socketserver
PORT = 8000
class CrossOriginIsolatedHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Cross-Origin-Opener-Policy', 'same-origin')
self.send_header('Cross-Origin-Embedder-Policy', 'require-corp')
super().end_headers()
def guess_type(self, path):
# Use mimetypes to guess type, fallback to default
base, ext = http.server.os.path.splitext(path)
if path.endswith(".wasm.br"):
return "application/wasm"
elif path.endswith(".js.br") :
return "application/javascript"
elif path.endswith(".data.br") :
return "application/octet-stream"
elif ext == ".br":
return "application/x-brotli"
return super().guess_type(path)
def send_head(self):
"""Override to add custom headers for certain filetypes"""
path = self.translate_path(self.path)
f = None
if http.server.os.path.isdir(path):
return super().send_head()
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except OSError:
self.send_error(404, "File not found")
return None
self.send_response(200)
self.send_header("Content-type", ctype)
# Add extra headers for specific file types
if path.endswith(".br"):
self.send_header("Content-Encoding", "br")
self.end_headers()
return f
Handler = CrossOriginIsolatedHandler
httpd = socketserver.ThreadingTCPServer(("", PORT), Handler)
print(f"Serving at http://localhost:{PORT}")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nShutting down server...")
httpd.shutdown()
this will run my brotii games and comply to the daft requirements that unity has.
Im no python dude, so Ive got some basis in there if you wanna add the normal compression too
(and i made it to go with)
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using UnityEditor;
using Debug = UnityEngine.Debug;
public class ApplicationPathExample
{
private static readonly Process p = new();
[MenuItem("Build/Info")]
private static void Info()
{
Debug.Log(
EditorUserBuildSettings.GetBuildLocation(EditorUserBuildSettings
.activeBuildTarget));
if (p.Responding) Debug.Log("Server is running");
}
[MenuItem("Build/Launch Last Built")]
private static void AppPath()
{
try
{
if (p.HasExited) StartServer();
}
catch (Exception e)
{
StartServer();
}
var b = new Process();
b.StartInfo.FileName = "http://localhost:8000/";
b.Start();
}
[MenuItem("Build/EndServer")]
private static void EndServer()
{
try
{
p.Kill();
Debug.Log("Server killed");
}
catch (Exception e)
{
Debug.Log(e.Message);
}
}
[MenuItem("Build/EndServer", true)]
public static bool ValidateDevMode()
{
// Return true to enable, false to disable
try
{
return (!p.HasExited);
}
catch (Exception e)
{
return false;
}
}
[MenuItem("Build/DevMode")]
private static void DevMode()
{
var current = UnityEditor.EditorPrefs.GetBool("DeveloperMode");
UnityEditor.EditorPrefs.SetBool("DeveloperMode", !current);
Debug.Log($"Devmode is now {(current ? "off" : "on")}");
}
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private static void StartServer()
{
Debug.Log("Starting server");
var apppath = Path.GetDirectoryName(EditorApplication.applicationPath);
var lastBuildPath =
EditorUserBuildSettings.GetBuildLocation(EditorUserBuildSettings
.activeBuildTarget);
p.StartInfo.FileName = @"c:\users\liz\Documents\Unity\http_server.py";
p.StartInfo.WorkingDirectory = lastBuildPath;
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = true;
p.EnableRaisingEvents = true;
p.Exited += (sender, args) =>
{
var proc = sender as Process;
if (proc != null)
{
if (proc.ExitCode != 0)
Debug.LogError(
$"Server crashed with exit code {proc.ExitCode}");
else
Debug.Log(
$"Server exited normally with exit code {proc.ExitCode}");
}
};
p.Start();
ShowWindow(p.MainWindowHandle, 6); // 6 minimize
Debug.Log($"Running PID: {p.Id}");
}
}
mgear
December 18, 2025, 9:11am
7
here’s also list of solutions,
Clicking “build and run” on a WebGL game opens http://localhost:52334/ and allows me to play test.
If i try to run the index file directly i get:
“It seems your browser does not support running Unity WebGL content from file:// urls. Please upload it to an http server, or try a different browser.”
What is Unity doing with the “Build and run” button and how do i replicate it so i can run folders with old builds?
personally im using custom server,
that i can launch from Explorer context menu,
also can share to LAN IP (so can connect the host from mobile devices),
and it supports https *but its not enabled by default, requires some windows setup initially)
minimal local c# webserver