Skip to content

Commit 0d1a872

Browse files
authored
Merge pull request #19 from gaarutyunov/claude/webgpu-charts-01HqVBtjZM96mB9zojKeHaeb
feat: Add WebGPU charting system with Bitcoin OHLCV example
2 parents 0920e55 + 4ab5b77 commit 0d1a872

32 files changed

Lines changed: 2871 additions & 36 deletions

.github/workflows/e2e.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,65 @@ jobs:
177177
examples/webgpu-cube/test-results/
178178
examples/webgpu-cube/tests/**/*-snapshots/
179179
retention-days: 7
180+
181+
webgpu-chart:
182+
name: WebGPU Chart E2E Tests
183+
runs-on: ubuntu-latest
184+
steps:
185+
- uses: actions/checkout@v4
186+
187+
- name: Set up Go
188+
uses: actions/setup-go@v5
189+
with:
190+
go-version: '1.25'
191+
192+
- name: Set up Node.js
193+
uses: actions/setup-node@v4
194+
with:
195+
node-version: '20'
196+
197+
- name: Build CLI
198+
run: go build -o guix ./cmd/guix
199+
200+
- name: Generate and build WebGPU chart example
201+
run: |
202+
# Generate Guix components
203+
./guix generate -p examples/webgpu-chart --verbose-logs
204+
205+
cd examples/webgpu-chart
206+
207+
# Copy wasm_exec.js from Go installation
208+
GOROOT="$(go env GOROOT)"
209+
if [ -f "$GOROOT/lib/wasm/wasm_exec.js" ]; then
210+
cp "$GOROOT/lib/wasm/wasm_exec.js" .
211+
elif [ -f "$GOROOT/misc/wasm/wasm_exec.js" ]; then
212+
cp "$GOROOT/misc/wasm/wasm_exec.js" .
213+
else
214+
echo "wasm_exec.js not found in GOROOT, downloading from GitHub"
215+
curl -o wasm_exec.js https://raw.githubusercontent.com/golang/go/go1.25.0/lib/wasm/wasm_exec.js
216+
fi
217+
218+
# Build WASM
219+
GOOS=js GOARCH=wasm go build -o main.wasm .
220+
echo "Files after build:"
221+
ls -lah *.{html,js,wasm} 2>/dev/null || ls -lah
222+
223+
- name: Install Playwright
224+
working-directory: examples/webgpu-chart
225+
run: |
226+
npm install
227+
npx playwright install --with-deps chromium
228+
229+
- name: Run WebGPU chart tests
230+
working-directory: examples/webgpu-chart
231+
run: npx playwright test
232+
233+
- name: Upload test results, videos, and screenshots
234+
if: always()
235+
uses: actions/upload-artifact@v4
236+
with:
237+
name: webgpu-chart-test-artifacts
238+
path: |
239+
examples/webgpu-chart/test-results/
240+
examples/webgpu-chart/tests/**/*-snapshots/
241+
retention-days: 7

examples/webgpu-chart/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
main.wasm
2+
wasm_exec.js
3+
node_modules/
4+
package-lock.json
5+
playwright-report/
6+
test-results/

examples/webgpu-chart/README.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# WebGPU Chart Example
2+
3+
This example demonstrates Guix's WebGPU-powered charting capabilities with a Bitcoin price candlestick chart.
4+
5+
## Features
6+
7+
- **GPU-accelerated rendering**: All chart elements rendered via WebGPU for high performance
8+
- **Candlestick charts**: OHLCV (Open-High-Low-Close-Volume) visualization
9+
- **Declarative syntax**: Define charts using Guix's component-based approach
10+
- **Automatic axis scaling**: Smart tick generation and formatting
11+
- **Responsive design**: Adapts to different screen sizes
12+
13+
## Prerequisites
14+
15+
- Go 1.21 or later
16+
- A WebGPU-enabled browser:
17+
- Chrome/Edge 113+
18+
- Firefox Nightly (with `dom.webgpu.enabled` flag)
19+
- Safari Technology Preview
20+
21+
## Building
22+
23+
### 1. Generate Guix Components
24+
25+
```bash
26+
go generate
27+
```
28+
29+
This will generate `app_gen.go` and `chart_gen.go` from the `.gx` source files.
30+
31+
### 2. Build WASM Binary
32+
33+
```bash
34+
GOOS=js GOARCH=wasm go build -o main.wasm
35+
```
36+
37+
### 3. Copy wasm_exec.js
38+
39+
```bash
40+
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
41+
```
42+
43+
## Running
44+
45+
Start a local web server:
46+
47+
```bash
48+
# Using Python
49+
python3 -m http.server 8080
50+
51+
# Or using Go
52+
go run ../../cmd/serve/main.go -port 8080
53+
```
54+
55+
Then open http://localhost:8080 in your WebGPU-enabled browser.
56+
57+
## Chart Components
58+
59+
The chart is defined declaratively in `chart.gx`:
60+
61+
```go
62+
chart.ChartNode(
63+
chart.ChartBackground(0.08, 0.09, 0.12, 1.0),
64+
chart.ChartPaddingProp(60, 20, 40, 80),
65+
66+
// X-Axis with time scale
67+
chart.XAxis(
68+
chart.TimeScale(true),
69+
chart.GridLines(true),
70+
),
71+
72+
// Y-Axis with price formatting
73+
chart.YAxis(
74+
chart.TickFormat(func(v float64) string {
75+
return chart.FormatCurrency(v, "USD")
76+
}),
77+
),
78+
79+
// Candlestick series
80+
chart.CandlestickSeries(
81+
chart.DataProp(data),
82+
chart.UpColor(0.18, 0.80, 0.44, 1.0), // Green
83+
chart.DownColor(0.91, 0.27, 0.38, 1.0), // Red
84+
),
85+
)
86+
```
87+
88+
## Data Format
89+
90+
OHLCV data is defined as:
91+
92+
```go
93+
type OHLCV struct {
94+
Timestamp int64 // Unix timestamp in milliseconds
95+
Open float64 // Opening price
96+
High float64 // Highest price
97+
Low float64 // Lowest price
98+
Close float64 // Closing price
99+
Volume float64 // Trading volume
100+
}
101+
```
102+
103+
## Browser Compatibility
104+
105+
| Browser | Version | Status |
106+
|---------|---------|--------|
107+
| Chrome | 113+ | ✅ Supported |
108+
| Edge | 113+ | ✅ Supported |
109+
| Firefox | Nightly | ⚠️ Experimental |
110+
| Safari | TP | ⚠️ Experimental |
111+
112+
## Troubleshooting
113+
114+
### WebGPU not available
115+
116+
If you see "WebGPU Not Supported", make sure you're using a compatible browser:
117+
118+
- **Chrome/Edge**: WebGPU is enabled by default in version 113+
119+
- **Firefox**: Enable `dom.webgpu.enabled` in `about:config`
120+
- **Safari**: Use Safari Technology Preview
121+
122+
### Chart not rendering
123+
124+
1. Check the browser console for errors
125+
2. Verify WebGPU is available: `navigator.gpu`
126+
3. Make sure the WASM binary loaded correctly
127+
4. Check that `wasm_exec.js` matches your Go version
128+
129+
## Architecture
130+
131+
The chart system follows Guix's declarative component model:
132+
133+
- **Chart Container** (`ChartNode`): Root container with background and padding
134+
- **Axes** (`XAxis`, `YAxis`): Configurable axes with scales and ticks
135+
- **Series** (`CandlestickSeries`): Data visualization components
136+
- **Shaders** (WGSL): GPU shaders for rendering chart elements
137+
138+
All rendering happens on the GPU via WebGPU for maximum performance.
139+
140+
## License
141+
142+
MIT License - see LICENSE file for details

examples/webgpu-chart/app.gx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
func App() (Component) {
4+
chartData := GetSampleData()
5+
6+
Div(
7+
ID("app"),
8+
Class("chart-container"),
9+
TabIndex(0)
10+
) {
11+
H1(Class("title")) {
12+
"Bitcoin Price Chart"
13+
}
14+
P(Class("subtitle")) {
15+
"WebGPU-powered candlestick chart"
16+
}
17+
Canvas(
18+
ID("chart-canvas"),
19+
Width(1200),
20+
Height(700)
21+
) {
22+
GPUChart(NewBitcoinChart(chartData))
23+
}
24+
Div(Class("info")) {
25+
P() {
26+
"This chart demonstrates Guix's WebGPU charting capabilities."
27+
}
28+
P() {
29+
"Data: Bitcoin daily OHLCV candles from December 2024"
30+
}
31+
}
32+
}
33+
}

examples/webgpu-chart/app_gen.go

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/webgpu-chart/chart.gx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package main
2+
3+
func BitcoinChart(data *ChartData) (Chart) {
4+
Chart(ChartBackground(0.08, 0.09, 0.12, 1.0)) {
5+
XAxis(AxisPosition("bottom"), TimeScale(true), GridLines(true))
6+
7+
YAxis(AxisPosition("right"), GridLines(true))
8+
9+
CandlestickSeries(
10+
ChartData(data.Bitcoin),
11+
UpColor(0.18, 0.80, 0.44, 1.0),
12+
DownColor(0.91, 0.27, 0.38, 1.0),
13+
WickColor(0.6, 0.6, 0.65, 1.0),
14+
BarWidth(0.8)
15+
)
16+
}
17+
}

examples/webgpu-chart/chart_gen.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/webgpu-chart/data.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//go:build js && wasm
2+
3+
package main
4+
5+
import "github.com/gaarutyunov/guix/pkg/runtime/chart"
6+
7+
// ChartData holds sample data for charts
8+
type ChartData struct {
9+
Bitcoin []chart.OHLCV
10+
}
11+
12+
// ChartConfig holds chart configuration
13+
type ChartConfig struct {
14+
Data []chart.OHLCV
15+
Title string
16+
Background ChartColor
17+
}
18+
19+
// ChartColor represents an RGBA color
20+
type ChartColor struct {
21+
R, G, B, A float32
22+
}
23+
24+
// NewChartConfig creates a new chart configuration with defaults
25+
func NewChartConfig() *ChartConfig {
26+
return &ChartConfig{
27+
Background: ChartColor{R: 0.08, G: 0.09, B: 0.12, A: 1.0},
28+
}
29+
}
30+
31+
// GetSampleData returns sample Bitcoin OHLCV data
32+
func GetSampleData() *ChartData {
33+
return &ChartData{
34+
Bitcoin: []chart.OHLCV{
35+
{Timestamp: 1701388800000, Open: 37500, High: 38200, Low: 37100, Close: 37800, Volume: 28500000000},
36+
{Timestamp: 1701475200000, Open: 37800, High: 39100, Low: 37600, Close: 38900, Volume: 32100000000},
37+
{Timestamp: 1701561600000, Open: 38900, High: 40200, Low: 38500, Close: 39800, Volume: 41200000000},
38+
{Timestamp: 1701648000000, Open: 39800, High: 41500, Low: 39200, Close: 41200, Volume: 45600000000},
39+
{Timestamp: 1701734400000, Open: 41200, High: 42100, Low: 40800, Close: 40500, Volume: 38900000000},
40+
{Timestamp: 1701820800000, Open: 40500, High: 41800, Low: 39900, Close: 41600, Volume: 35200000000},
41+
{Timestamp: 1701907200000, Open: 41600, High: 43500, Low: 41200, Close: 43200, Volume: 52100000000},
42+
{Timestamp: 1701993600000, Open: 43200, High: 44100, Low: 42500, Close: 43800, Volume: 48700000000},
43+
{Timestamp: 1702080000000, Open: 43800, High: 44500, Low: 42200, Close: 42800, Volume: 39400000000},
44+
{Timestamp: 1702166400000, Open: 42800, High: 43900, Low: 41800, Close: 43500, Volume: 36800000000},
45+
{Timestamp: 1702252800000, Open: 43500, High: 44200, Low: 42900, Close: 43100, Volume: 34200000000},
46+
{Timestamp: 1702339200000, Open: 43100, High: 44800, Low: 42800, Close: 44600, Volume: 42800000000},
47+
{Timestamp: 1702425600000, Open: 44600, High: 45500, Low: 43900, Close: 45200, Volume: 46100000000},
48+
{Timestamp: 1702512000000, Open: 45200, High: 46100, Low: 44800, Close: 45800, Volume: 43900000000},
49+
{Timestamp: 1702598400000, Open: 45800, High: 46500, Low: 44900, Close: 45100, Volume: 38700000000},
50+
{Timestamp: 1702684800000, Open: 45100, High: 45900, Low: 44200, Close: 44800, Volume: 35600000000},
51+
{Timestamp: 1702771200000, Open: 44800, High: 46200, Low: 44500, Close: 46000, Volume: 41200000000},
52+
{Timestamp: 1702857600000, Open: 46000, High: 47100, Low: 45700, Close: 46900, Volume: 48300000000},
53+
{Timestamp: 1702944000000, Open: 46900, High: 47800, Low: 46200, Close: 47500, Volume: 51200000000},
54+
{Timestamp: 1703030400000, Open: 47500, High: 48200, Low: 46800, Close: 47100, Volume: 44800000000},
55+
},
56+
}
57+
}

examples/webgpu-chart/gen.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build js && wasm
2+
3+
package main
4+
5+
//go:generate guix generate -p .

0 commit comments

Comments
 (0)