https://github.com/crgimenes/glaze
Glaze: a CGO-free desktop WebView toolkit for Go, with practical helpers for building native app windows from web UIs.
https://github.com/crgimenes/glaze
cgo-free cross-platform desktop go golang purego webview
Last synced: 2 months ago
JSON representation
Glaze: a CGO-free desktop WebView toolkit for Go, with practical helpers for building native app windows from web UIs.
- Host: GitHub
- URL: https://github.com/crgimenes/glaze
- Owner: crgimenes
- License: mit
- Created: 2026-02-09T10:11:38.000Z (4 months ago)
- Default Branch: master
- Last Pushed: 2026-03-31T14:18:47.000Z (3 months ago)
- Last Synced: 2026-03-31T16:25:00.501Z (3 months ago)
- Topics: cgo-free, cross-platform, desktop, go, golang, purego, webview
- Language: Go
- Homepage:
- Size: 81 MB
- Stars: 10
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Glaze
Glaze is a pure Go desktop WebView binding built on top of [webview/webview](https://github.com/webview/webview) and [purego](https://github.com/ebitengine/purego).
This repository started from the original `go-webview` project, but it has diverged significantly and is maintained as a separate codebase with its own goals and APIs.
## Goals
- Keep a CGo-free desktop integration layer for Go applications.
- Provide pragmatic helpers for desktop app workflows.
- Keep behavior explicit and testable.
- Stay friendly to multi-module `go.work` development.
## Key Features
- No CGo
- Windows, macOS, and Linux support
- Native library loading with embedded runtime assets
- JavaScript to Go binding support
- Utility helpers for desktop app patterns: `BindMethods`, `RenderHTML`, `AppWindow`
## Examples
| Desktop | Game of Life | Starfield |
| --- | --- | --- |
| [](examples/desktop/) | [](examples/gameoflife/) | [](examples/starfield/) |
| Doom Fire | Mandelbrot | Falling Sand |
| --- | --- | --- |
| [](examples/doomfire/) | [](examples/mandelbrot/) | [](examples/fallingsand/) |
| Raycasting | Filo REPL | |
| --- | --- | --- |
| [](examples/raycasting/) | [](examples/filorepl/) | |
## Install
```bash
go get github.com/crgimenes/glaze@latest
```
To use embedded native libraries:
```go
import _ "github.com/crgimenes/glaze/embedded"
```
## Quick Start
```go
package main
import (
"log"
"github.com/crgimenes/glaze"
_ "github.com/crgimenes/glaze/embedded"
)
func main() {
w, err := glaze.New(true)
if err != nil {
log.Fatal(err)
}
defer w.Destroy()
w.SetTitle("Glaze")
w.SetSize(800, 600, glaze.HintNone)
w.SetHtml("
Hello from Glaze
")
w.Run()
}
```
Glaze pins the goroutine that creates the first window to its current OS thread.
Keep direct window calls on that goroutine, and use `Dispatch` to re-enter the UI
thread from background work.
## Desktop Helpers
### BindMethods
`BindMethods` is a convenience layer over `Bind` that exposes all exported
methods of a Go value as JavaScript-callable functions.
What it does:
- Reflects over exported methods on a struct or pointer receiver.
- Builds JavaScript names using a prefix and snake_case conversion.
- Example: `GetUserByID` with prefix `api` becomes `api_get_user_by_id`.
- Applies the same function signature rules as `Bind`:
- no return
- value
- error
- value and error
- Returns the list of bound names so you can log or verify registration.
This is useful when you have a service object and want to expose a consistent
JavaScript API without writing one `Bind` call per method.
```go
type Store struct{}
func (s *Store) GetItems() []string { return []string{"a", "b"} }
bound, err := glaze.BindMethods(w, "store", &Store{})
```
### RenderHTML
`RenderHTML` renders a named Go `html/template` into a string for `SetHtml`.
What it does:
- Executes a specific template by name (including nested template calls).
- Returns the final HTML string.
- Wraps template execution errors with template context.
This is useful when you want server-style template rendering in a local desktop
app without running an HTTP server for that page.
```go
html, err := glaze.RenderHTML(tpl, "page", data)
if err != nil {
return err
}
w.SetHtml(html)
```
### AppWindow
`AppWindow` wraps an `http.Handler` inside a native desktop window backed by a
local loopback HTTP server.
What it does:
- Supports selectable transport with platform-aware default:
- `auto` (default): `unix` on macOS/Linux, `tcp` on Windows
- `tcp`: direct loopback HTTP (`127.0.0.1`)
- `unix`: handler served on Unix socket with a lightweight loopback HTTP
gateway for browser navigation
- Starts listeners using random free ports/paths by default (or custom
`Addr`/`UnixSocketPath`).
- Creates a native window and navigates it to that local URL.
- Runs the UI loop and closes the HTTP server when the window exits.
- Supports window sizing, title, debug mode, and optional readiness callback.
- `OnReady`: receives browser URL (always `http://127.0.0.1:...`).
- `OnReadyInfo`: receives resolved backend details (`Transport`, `Backend`,
`Gateway`) so you can verify unix vs tcp in logs.
This is the simplest way to reuse an existing `net/http` application as a
desktop app with minimal changes to your routing, templates, and assets.
```go
err := glaze.AppWindow(glaze.AppOptions{
Title: "My App",
Width: 1280,
Height: 800,
Transport: glaze.AppTransportAuto,
Handler: mux,
OnReadyInfo: func(info glaze.AppReadyInfo) {
log.Printf("transport=%s backend=%s gateway=%s", info.Transport, info.Backend, info.Gateway)
},
})
```
## Running Examples
From the repository root:
```bash
go run ./examples/simple
go run ./examples/bind
go run ./examples/zero_tcp
```
From each example directory:
```bash
cd examples/appwindow && go run .
cd examples/desktop && go run .
cd examples/filorepl && go run .
```
`examples/zero_tcp` demonstrates a local-first UI with `SetHtml + BindMethods`
only. It does not start an HTTP server, so there is no loopback TCP gateway.
## Testing
Default tests (headless safe):
```bash
go test ./...
```
GUI integration test:
```bash
go test -tags=integration -run TestWebview ./...
```
## Building on Windows
Use `windowsgui` to hide the console window:
```bash
go build -ldflags="-H windowsgui" .
```
## Project Layout
- `webview.go` - core API and binding internals
- `appwindow.go` - desktop window plus local HTTP server helper
- `helpers.go` - utility helpers (`BindMethods`, `RenderHTML`)
- `embedded/` - embedded native library assets per platform
- `examples/` - runnable sample applications
## Security: Library Integrity Verification
Glaze embeds native libraries and extracts them to disk before loading. By
default, the extraction target is a temporary directory that may be writable by
other processes, which could allow an attacker to replace the library file.
To mitigate this, Glaze computes a BLAKE2b-256 hash of the embedded library
bytes at runtime and verifies every extracted (or pre-existing) file against
that hash before loading. If the hash does not match, extraction fails with an
integrity error and the library is **not** loaded.
Additionally, extracted files are created with restricted permissions (`0500`
owner read+execute) inside a directory with `0700` permissions.
### Custom Library Directory
For production deployments, use `ExtractTo` to place the library in a secure,
application-controlled directory instead of the system temp directory:
```go
package main
import (
"log"
"github.com/crgimenes/glaze"
"github.com/crgimenes/glaze/embedded"
)
func main() {
// Extract to a directory with restricted access.
if err := embedded.ExtractTo("/opt/myapp/lib"); err != nil {
log.Fatal(err)
}
w, err := glaze.New(true)
if err != nil {
log.Fatal(err)
}
defer w.Destroy()
w.SetTitle("Secure App")
w.SetSize(800, 600, glaze.HintNone)
w.SetHtml("
Hello
")
w.Run()
}
```
When using `ExtractTo`, do **not** use `import _ "github.com/crgimenes/glaze/embedded"` — that
blank import triggers `init()` which extracts to the default temp directory.
Call `ExtractTo` explicitly instead.
## Acknowledgments
- [abemedia/go-webview](https://github.com/abemedia/go-webview) for the original Go binding base
- [webview/webview](https://github.com/webview/webview) for the native WebView implementation
- [purego](https://github.com/ebitengine/purego) for dynamic linking without CGo