Co-authored-by: Copilot <copilot@github.com>
9.0 KiB
Canvas API - Hierarchical Image Fallback System
Overview
The Canvas API provides intelligent image serving with automatic fallback across multiple directory levels. This eliminates image duplication and makes it easy to share common elements (like foreground plants or backgrounds) across multiple products.
Architecture
URL Pattern
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
Directory Structure
app/static/images/
├── doors/
│ ├── storm/
│ │ ├── 404/
│ │ │ ├── base.jpg # Product-specific base
│ │ │ ├── door-white.png # Product-specific door color
│ │ │ └── door-black.png
│ │ ├── 505/
│ │ │ ├── base.jpg
│ │ │ └── door-white.png
│ │ └── layers/
│ │ ├── base.jpg # Fallback for ALL storm doors
│ │ ├── hardware.png # Shared hardware
│ │ └── foreground.png # Shared foreground
│ ├── entry/
│ │ ├── 600/
│ │ │ ├── base.jpg
│ │ │ └── door-bronze.png
│ │ └── layers/
│ │ └── foreground-plants.png # Shared for entry doors
│ └── layers/
│ ├── hardware-generic.png # Fallback for ALL doors
│ └── foreground-default.png
├── windows/
│ ├── storm/
│ │ └── layers/
│ │ └── foreground-minimal.png
│ └── layers/
│ └── base-generic.jpg
└── layers/
├── foreground-default.png # Global fallback
├── base-generic.jpg # Global fallback
└── hardware-generic.png # Global fallback
Fallback Priority
When requesting an image, the system searches in this order (most specific to least specific):
- Product-specific with variant:
/images/<type>/<subtype>/<code>/<layer>-<color>.png - Product-specific:
/images/<type>/<subtype>/<code>/<layer>.png - Subtype fallback with variant:
/images/<type>/<subtype>/layers/<layer>-<color>.png - Subtype fallback:
/images/<type>/<subtype>/layers/<layer>.png - Type fallback:
/images/<type>/layers/<layer>.png - Global fallback:
/images/layers/<layer>.png - 404: Image not found
API Endpoints
Get Layer Image
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
Parameters:
product_code(path, required): Product code (e.g., "404", "505")layer(path, required): Layer name - must be one of:base,door,hardware,overlay,foregroundcolor(query, optional): Color variant (e.g., "white", "black", "bronze")material(query, optional): Material variant (for future use)
Response:
- Success: Image file (PNG, JPG, JPEG, or WebP)
- Error 400: Invalid layer name
- Error 404: Image not found
Examples:
GET /api/canvas/404/base
GET /api/canvas/404/door?color=white
GET /api/canvas/505/hardware
GET /api/canvas/600/foreground
Get Canvas Info
GET /api/canvas/<product_code>/info
Returns metadata about available layers for a product.
Response:
{
"product": "404",
"type": "window",
"subtype": "storm-window",
"availableLayers": {
"base": true,
"door": true,
"door_colors": ["white", "black", "bronze", "sandstone"],
"hardware": true,
"foreground": true
},
"colors": ["White", "Black", "Bronze", "Sandstone"],
"materials": ["Aluminum"]
}
Test Endpoint
GET /api/canvas/test
Verifies the Canvas API is running.
Product Configuration
Enable Canvas API
Add "useCanvasAPI": true to the product's imageConfig:
{
"productCode": "404",
"description": "#404 FALCON STORM WINDOWS",
"colors": ["White", "Black", "Bronze", "Sandstone"],
"materials": ["Aluminum"],
"imageConfig": {
"useCanvasAPI": true
}
}
That's it! No need to specify paths or layers - the Canvas API will automatically find them using the fallback system.
Optional: Legacy Layered Configuration
If you're migrating from the old static layered system, you can keep both configurations during transition:
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true,
"layered": true,
"basePath": "images/products/404/",
"layers": {
"base": "base.jpg",
"door": {
"white": "door-white.png",
"black": "door-black.png"
}
}
}
}
File Organization Strategy
Product-Specific Files
Place in /images/<type>/<subtype>/<code>/:
- Unique base images with specific backgrounds
- Color variants that are product-specific
- Custom hardware or features
Subtype Shared Files
Place in /images/<type>/<subtype>/layers/:
- Common hardware for that subtype
- Shared foreground elements (plants, railings)
- Default base images for that subtype
Type Shared Files
Place in /images/<type>/layers/:
- Generic hardware for all products of that type
- Common decorative elements
Global Shared Files
Place in /images/layers/:
- Universal fallback images
- Default placeholder elements
Migration Guide
From Static Layered System
Before:
app/static/images/
└── products/
├── 404/
│ ├── base.jpg
│ ├── door-white.png
│ ├── door-black.png
│ ├── hardware.png
│ └── plants.png
└── 505/
├── base.jpg
├── door-white.png
├── hardware.png # Duplicate!
└── plants.png # Duplicate!
After:
app/static/images/
└── doors/
└── storm/
├── 404/
│ ├── base.jpg
│ ├── door-white.png
│ └── door-black.png
├── 505/
│ ├── base.jpg
│ └── door-white.png
└── layers/
├── hardware.png # Shared by both!
└── plants.png # Shared by both!
Update JSON:
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true // Enable Canvas API
}
}
Migration Steps
- Identify shared elements: Look for duplicate files across products
- Reorganize directory: Move files to appropriate fallback levels
- Update product JSON: Add
"useCanvasAPI": true - Test: Verify images load correctly
- Cleanup: Remove old duplicate files
Development Tips
Testing Fallback Behavior
- Start with global fallback layer
- Test product without specific file - should show global fallback
- Add subtype-specific layer - should override global
- Add product-specific layer - should override subtype
Debugging
Check Flask logs to see which image path was found:
[INFO] Found image: /path/to/images/doors/storm/404/base.jpg
If image not found, logs show what was searched:
[WARNING] No image found for 404/door (color: white)
Browser Testing
Open browser console to see Canvas API requests:
GET /api/canvas/404/base → 200 OK
GET /api/canvas/404/door?color=white → 200 OK
GET /api/canvas/404/foreground → 200 OK (from fallback)
Performance Considerations
Advantages
- Reduced file duplication: Share common elements across products
- Smaller total file size: No duplicate hardware/foreground images
- Easier maintenance: Update one shared file affects all products
- Smart caching: Browser caches shared layers for faster loading
Best Practices
- Optimize images before uploading (TinyPNG, ImageOptim)
- Use appropriate formats:
- JPG for base layers (no transparency needed)
- PNG for layers with transparency
- WebP for modern browsers (optional)
- Keep file sizes reasonable:
- Base: 200-500KB
- Layers: 50-200KB each
- Foreground: 50-150KB
Troubleshooting
Image Not Showing
- Check product's
baseTypeandsubTypein products.json - Verify file exists in correct directory structure
- Check Flask logs for the search path
- Ensure product has
"useCanvasAPI": true
Wrong Image Displayed
- Check fallback priority - more specific path should override generic
- Verify file naming matches expected pattern
- Check that color parameter matches filename (lowercase)
404 Errors
- Open
/api/canvas/<product>/infoto see what's available - Check file permissions
- Verify directory structure matches expected pattern
Future Enhancements
Potential additions to the Canvas API system:
- Dynamic color tinting (apply color to grayscale base)
- Image composition/overlays
- Real-time layer opacity/blend modes
- A/B testing different foreground elements
- Seasonal foreground rotation
- Material-based texture overlays