A demonstration Bitcoin candlestick chart built with WebGPU for rendering and Leptos for the reactive UI. Real-time price data is streamed from Binance via WebSocket and drawn directly to a <canvas> using Rust compiled to WebAssembly.
The chart supports zoom levels from roughly 0.2x up to 32x with a minimum of one visible candle.
Note: WebGPU must be enabled in your browser. The demo works in Microsoft Edge but is not supported in Firefox.
The development version is available at https://qqrm.github.io/webgpu-candles/dev/, and release builds
are published at https://qqrm.github.io/webgpu-candles/. GitHub Pages publishes these files from the
gh-pages branch, keeping build artifacts out of main.
The project requires the wasm32-unknown-unknown target, which the build script verifies is installed. Install it with:
rustup target add wasm32-unknown-unknown.
# Add the WebAssembly compilation target
rustup target add wasm32-unknown-unknown
# Install Trunk for building and serving
cargo install trunkInstall either Trunk or wasm-pack depending on your preferred workflow.
To automatically format and lint the code before each commit, enable the pre-commit hook:
git config core.hooksPath .githooksTrunk compiles the project and automatically injects the generated WASM into index.html:
trunk serve # dev server on http://localhost:8080
# or
trunk build --dist dist-localLocal builds are saved to dist-local. In GitHub Actions the dist path is
uploaded to the gh-pages branch to publish the demo. The dist/ directory is
not stored in the repository; the gh-pages branch contains the version
file with the SHA of the last commit.
Both release and development builds are deployed to gh-pages. To use a different location, adjust the deployment steps in the workflow files:
.github/workflows/build.yml and .github/workflows/release.yml.
When using Trunk, open index.html (served automatically when using trunk serve). The file contains a Trunk hook so the WASM is loaded for you:
<!-- Trunk will automatically inject the WASM here -->
<link data-trunk rel="rust" data-wasm-opt="z" />Trunk automatically includes integrity hashes for the generated JavaScript and WebAssembly files.
Alternatively, you can build using wasm-pack:
wasm-pack build --target web --releaseThis produces a pkg/ directory with the compiled price_chart_wasm.js. After running wasm-pack, open leptos-index.html, which manually imports the generated file:
<script type="module">
import init, { hydrate } from './pkg/price_chart_wasm.js';
// ...
</script>Key folders are under src/:
src/
├── app.rs # Leptos UI components and reactivity
├── lib.rs # WASM exports (entry points)
├── simple_shader.wgsl # WebGPU shaders
├── domain/ # Core domain logic (chart, market data, logging)
├── infrastructure/ # WebSocket and WebGPU renderer implementations
For more architectural details see ARCHITECTURE.md. Planned features are listed in FEATURES.md. Details on the WebSocket feed are in WEBSOCKETS.md.
All additional documentation lives in the DOCS/ directory:
- ARCHITECTURE.md
- FEATURES.md
- WEBSOCKETS.md
- CONTRIBUTING.md
- PIPELINES.md
- TESTS.md
- PIPELINES.md
- VOLUME_SYNC_FIXES.md
- COLORS.md
The chart is composed of several layers:
- Candles with wicks representing OHLC data
- Volume bars below the main chart
- Time and price grid lines
- A highlighted line for the current price
- Technical indicators:
- Simple Moving Averages (20, 50, 200 periods)
- Exponential Moving Averages (12, 26 periods)
- Ichimoku cloud with Tenkan, Kijun, Senkou spans and Chikou line
To measure performance use:
wasm-pack test --chrome --headlessFPS is printed to the console and the perf.yml workflow saves the log as an
artifact. Current metric values are stored in PIPELINES.md.
tests/performance_limit.rs logs when FPS drops below 30 for large charts.
The tests use wasm-bindgen-test. Run
them with:
wasm-pack test --chrome --headless
or
wasm-pack test --chromeAlternatively install Node dependencies and run:
npm install
npm testSee TESTS.md for more details about the test suite.
For benchmarking outside the browser you can run the native binary. Parallel ECS systems powered by Rayon are enabled automatically:
cargo run --release --features parallelBuild and run the container with:
docker build -t webgpu-candles .
docker run --rm -p 8080:80 webgpu-candles(the container uses nginx, so port 80 is mapped to host 8080).
npx wscat -c wss://stream.binance.com:9443/ws/btcusdt@kline_1mThis project is distributed under the MIT License.
