This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
JavaCanvas is a Java implementation of the HTML5 Canvas 2D API with dual graphics backend support (AWT/Swing and JavaFX) and JavaScript integration via Mozilla Rhino and GraalJS. The project is 100% feature complete with all 149 tests passing.
# Build with Maven (primary build tool)
mvn clean package
# Or use Maven wrapper
./mvnw clean package
# Build with Gradle (alternative)
./gradlew build # Linux/Mac/Windows
.\gradlew.ps1 build # Windows PowerShell alternative
# Build native image with GraalVM
mvn -Pnative package
# OR
./gradlew nativeCompileGradle Configuration: This project uses a project-local .gradle directory (already in .gitignore) instead of the user's home folder. This avoids Windows issues with special characters in usernames (e.g., Char'les). Both gradlew.bat and gradlew.ps1 automatically set GRADLE_USER_HOME to the project's .gradle directory unless explicitly overridden.
If you need to use a shared Gradle home (rare), use .\gradle-shared.ps1 on Windows.
Important Gradle Notes:
- JavaFX version must match Java toolchain version (17.0.13 for Java 17)
- Monocle version must also match (17.0.10 for Java 17)
- All TestFX/Prism system properties from pom.xml must be in build.gradle test configuration for headless testing
# Run all tests (uses xvfb for headless testing on Linux)
./run-tests.sh
# Run tests directly with Maven
mvn test
# Run specific test class
mvn test -Dtest=TestCanvas2D
# Run specific test method
mvn test -Dtest=TestCanvas2D#testFillRect
# Generate coverage report (view at target/site/jacoco/index.html)
mvn clean testImportant Testing Notes:
- Tests require a headless environment (xvfb on Linux) for JavaFX/AWT rendering
- On Windows, tests may run directly but headless mode is recommended for CI
- Maven is configured with headless system properties in pom.xml
- All 149 tests must pass before committing changes
# Run with Rhino JavaScript engine
java -cp target/javacanvas-1.0-SNAPSHOT.jar com.w3canvas.javacanvas.Main script.js
# Run with GraalJS engine
java -cp target/javacanvas-1.0-SNAPSHOT.jar com.w3canvas.javacanvas.Main --graal script.jsJavaCanvas uses a three-layered architecture that cleanly separates concerns:
Pure Java interfaces defining the contract for the canvas system. No implementation code.
Key Interfaces:
ICanvasRenderingContext2D- Main Canvas 2D API contractIGraphicsBackend- Factory for creating backend-specific components (surfaces, gradients, fonts)IGraphicsContext- Low-level rendering operations (draw, fill, stroke)ICanvasSurface- Represents a drawable surface with pixel data accessIPaint,IFont,IShape,IStroke,IComposite- Rendering primitivesICanvasGradient,ICanvasPattern- Fill stylesIPath2D,IImageData,IImageBitmap,ITextMetrics- Canvas objects
Backend-agnostic implementation of Canvas 2D business logic. Never imports AWT or JavaFX classes.
Key Classes:
CoreCanvasRenderingContext2D- Complete Canvas 2D API implementation- Maintains rendering state (fillStyle, strokeStyle, lineWidth, etc.)
- Manages state stack for save/restore
- Delegates rendering to IGraphicsContext (backend-agnostic)
- Handles Path2D, filters, shadows, compositing
Path2D- Reusable path objectsImageData,ImageBitmap- Image manipulationTextMetrics- Text measurement resultsColorParser,CSSFilterParser- CSS parsing utilitiesCompositeFactory- Creates composite operations
Critical Architecture Rule: Core layer must remain backend-agnostic. All rendering operations go through IGraphicsBackend and IGraphicsContext interfaces.
Production-ready AWT/Swing implementation. This is the primary, fully-featured backend.
Key Classes:
AwtGraphicsBackend- Factory for AWT componentsAwtGraphicsContext- Wraps Graphics2D for renderingAwtCanvasSurface- BufferedImage-based surfaceAwt*classes - AWT-specific implementations of gradients, patterns, fonts, etc.
AWT Backend Specifics:
- Uses Graphics2D for all rendering
- Custom Paint implementations for conic gradients (AwtConicGradient)
- Approximations for some CSS blend modes (hue, saturation, color, luminosity)
- TextLayout for advanced text features (letter spacing, direction)
Alternative implementation using JavaFX graphics. Less feature-complete than AWT.
Key Classes:
JavaFXGraphicsBackend- Factory for JavaFX componentsJavaFXGraphicsContext- Wraps GraphicsContext for renderingJavaFXCanvasSurface- Canvas-based surfaceJavaFX*classes - JavaFX-specific implementations
JavaFX Backend Limitations:
- Some Porter-Duff operations fall back to SRC_OVER
- Advanced text properties (direction, spacing) stored but not fully rendered
- Limited composite mode support compared to AWT
Bridges Core implementations to Mozilla Rhino JavaScript engine. Provides DOM-like APIs.
Key Classes:
CanvasRenderingContext2D- Rhino scriptable wrapper around CoreCanvasRenderingContext2DHTMLCanvasElement- JavaScript canvas elementDocument- JavaScript document objectWindow- JavaScript global window object- Gradient, Pattern, ImageData wrappers for Rhino
JavaScript Integration:
- All Rhino adapter classes extend Scriptable/ScriptableObject
- Adapters delegate to Core layer implementations
- Provides HTML5-like JavaScript API surface
The Canvas 2D API maintains extensive state (fillStyle, strokeStyle, transformations, etc.). State is managed in CoreCanvasRenderingContext2D and can be saved/restored via a stack:
// State is stored in ContextState inner class
save(); // Pushes current state onto stack
// ... modify state ...
restore(); // Pops state from stackAll rendering goes through interfaces to support multiple backends:
// CORRECT: Use backend interface
IGraphicsContext gc = surface.getGraphicsContext();
gc.fillRect(x, y, width, height);
// WRONG: Don't use AWT/JavaFX directly in Core layer
Graphics2D g = ...; // Never in Core layer!Graphics contexts must be properly disposed:
// AwtCanvasSurface.getGraphicsContext() creates a new Graphics2D
// Caller must call dispose() when done, OR use surface's internal gc
IGraphicsContext gc = surface.getGraphicsContext();
try {
// ... rendering ...
} finally {
gc.dispose(); // Critical for AWT backend
}Filters are parsed as CSS strings and applied as BufferedImageOp:
ctx.setFilter("blur(5px) brightness(120%)");
// CSSFilterParser extracts filter functions
// Applied during rendering operations in backend-
Graphics Disposal: AWT Graphics2D objects must be disposed. The AwtCanvasSurface maintains an internal graphics context that gets disposed on reset().
-
Backend Differences: Not all features work identically across backends. AWT is the reference implementation. Always test with both backends when adding features.
-
Headless Testing: JavaFX requires special headless configuration (xvfb, monocle). Tests include
@Timeoutannotations because headless initialization is slow. -
Rhino vs Core: When adding Canvas API features:
- Implement in Core layer first (backend-agnostic)
- Add backend implementations (AWT, JavaFX)
- Add Rhino wrappers last (thin delegation)
-
Path2D Transform Application: Paths store their own transforms. When rendering a Path2D, combine its transform with the current context transform.
-
State Stack: The state stack stores deep copies of state, including transformation matrices. Never modify state objects after pushing to stack.
Tests are organized by functionality:
TestCanvas2D- 77 comprehensive Canvas 2D API tests (core rendering, transforms, paths)TestImageBitmap- ImageBitmap API (11 tests)TestOffscreenCanvas- OffscreenCanvas API (10 tests)TestCSSFilters- CSS filter parsing (18 tests)TestFilterIntegration- Filter rendering integration (10 tests)TestSharedWorker,TestWorker- Web Worker APIsTestJavaFX,TestAwtBackendSmokeTest- Backend-specific tests- Font and integration tests
Test Patterns:
- Use
VisualRegressionHelper.compareToGoldenMaster()for pixel-perfect rendering validation - Golden masters stored in
src/test/resources/golden-masters/ - Generate new baselines with
-DgenerateGoldenMasters=true - Tests extend
ApplicationTestfor JavaFX integration - Proper synchronization (no Thread.sleep) for async operations
-
Add to Interface (
ICanvasRenderingContext2D)void newFeature(double param);
-
Implement in Core (
CoreCanvasRenderingContext2D)@Override public void newFeature(double param) { // State management, validation gc.drawNewFeature(param); // Delegate to backend }
-
Add Backend Method (
IGraphicsContext)void drawNewFeature(double param);
-
Implement in Backends (
AwtGraphicsContext,JavaFXGraphicsContext)@Override public void drawNewFeature(double param) { // AWT or JavaFX specific rendering }
-
Add Rhino Wrapper (
CanvasRenderingContext2D)public void jsFunction_newFeature(double param) { coreContext.newFeature(param); }
-
Write Tests (
TestCanvas2D)@Test void testNewFeature() { ctx.newFeature(100); // Verify rendering }
- JavaDoc: All public interfaces and classes must have complete JavaDoc
- Parameter Validation: Validate parameters with descriptive IllegalArgumentException messages
- Resource Management: Always dispose Graphics2D contexts
- No printStackTrace: Use proper logging or throw exceptions
- Error Handling: Meaningful error messages for users
- Test Coverage: All new features must have tests
- No Magic Numbers: Extract as named constants
- Backend Agnostic Core: Core layer never imports java.awt or javafx packages
- README.md - Project overview, features, usage examples
- STATUS.md - Current status, test results, known limitations
- REFACTOR.md - Architectural design and Trident model explanation
- TESTING.md - Complete testing guide and test suite breakdown
- IMPROVEMENTS.md - Code quality recommendations and priorities