A modern web-based canvas editor for creating stunning visual designs. Upload images, add text overlays, customize backgrounds, and export high-quality graphicsβall in your browser.
- Image Upload - Drag & drop or file picker for image uploads
- Website Screenshots - Capture screenshots of websites via URL using the free Screen-Shot.xyz API (no API key required)
- Image Transformations - Scale, opacity, rotation, and border radius controls
- 3D Perspective (coming soon) - Apply 3D transforms with perspective effects
- Borders - Multiple border styles (glassy, window, ruler, eclipse, dotted, focus, and more)
- Shadows - Customizable shadows with blur, offset, spread, and color controls
- Text Overlays - Add multiple text layers with independent positioning
- Custom Fonts - Choose from a variety of font families
- Text Styling - Customize font size, weight, color, and opacity
- Text Shadows - Add shadows to text with customizable properties
- Image Overlays - Decorative overlays from gallery or custom uploads
- Overlay Controls - Position, size, rotation, flip, and opacity controls
- Gradient Backgrounds - Beautiful gradient presets with customizable colors and angles
- Solid Colors - Choose from a palette of solid color backgrounds
- Image Backgrounds - Upload your own or use Cloudinary-hosted backgrounds
- Background Effects - Apply blur and noise effects to backgrounds
- Aspect Ratios - Support for Instagram, social media, and standard formats
- Square (1:1), Portrait (4:5, 9:16), Landscape (16:9)
- Open Graph, Twitter Banner, LinkedIn Banner, YouTube Banner
- Presets - One-click professional styling presets
- Export Options - PNG format with adjustable quality (0-1) and scale (up to 5x)
- Fully In-Browser Export - All export operations run client-side without external services
- Copy to Clipboard - Copy designs directly to clipboard
- Responsive Design - Works seamlessly on desktop and mobile
- Real-time Preview - See changes instantly as you edit
- Local Storage - Designs persist in browser storage
- High-Quality Export - Export at up to 5x scale for high-resolution output
- Node.js 18+ and npm
- Git
-
Clone the repository
git clone https://github.com/your-username/stage.git cd stage -
Install dependencies
npm install
-
Set up environment variables
Create a
.env.localfile in the root directory:# Database (Required for screenshot caching) DATABASE_URL="postgresql://user:password@host:port/dbname?schema=public" # Cloudinary (Required for screenshot storage) NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=dlbe0tua7 CLOUDINARY_API_KEY=your-api-key CLOUDINARY_API_SECRET=your-api-secret # Cache Cleanup Security (Required for production) CLEANUP_SECRET=your-random-secret-string # Screenshot API URL (Optional - defaults to free Screen-Shot API) # Uses https://api.screen-shot.xyz by default (free, no API key required) # You can deploy your own instance or use a different service SCREENSHOT_API_URL=https://api.screen-shot.xyz
Note: Screenshot feature requires database and Cloudinary. All other core features including export work fully in-browser. Cloudinary is also used for optional image optimization of backgrounds and overlays. The screenshot API uses the free Screen-Shot.xyz service by default (no API key required).
-
Set up the database
# Run Prisma migrations to create the database schema npx prisma migrate dev --name init # Or use db push for quick setup (no migration files) npx prisma db push
-
Start the development server
npm run dev
-
Open your browser
Navigate to http://localhost:3000
-
Upload an Image
- Drag and drop an image onto the canvas, or
- Click to browse and select a file, or
- Enter a website URL to capture a screenshot
-
Customize Your Design
- Adjust image properties (scale, opacity, border radius)
- Choose a background (gradient, solid color, or image)
- Add text overlays with custom styling
- Add decorative image overlays
- Apply borders and shadows
- Use 3D perspective transforms
-
Select Aspect Ratio
- Choose from various aspect ratios for different use cases
- Instagram posts, stories, social media banners, etc.
-
Export Your Design
- Click the export button
- Adjust quality and scale settings
- Download as PNG or copy to clipboard
- All export processing happens in your browser - no external services required
- Next.js 16 - React framework with App Router
- React 19 - UI library with React Compiler
- TypeScript - Type safety
- Konva - 2D canvas rendering engine
- React-Konva - React bindings for Konva
- html2canvas - DOM-to-canvas conversion
- modern-screenshot - 3D transform capture
- Zustand - Lightweight state management
- Tailwind CSS 4 - Utility-first CSS framework
- Radix UI - Accessible component primitives
- Lucide React - Icon library
- Cloudinary - Image optimization, CDN, and screenshot storage
- Sharp - Server-side image processing
- Screen-Shot.xyz API - Free website screenshot capture service (no API key required)
- Prisma - Type-safe ORM
- PostgreSQL - Database for screenshot caching
stage/
βββ app/ # Next.js pages and API routes
β βββ api/ # API endpoints
β βββ home/ # Editor page
β βββ page.tsx # Landing page
βββ components/ # React components
β βββ canvas/ # Canvas rendering
β βββ controls/ # Editor controls
β βββ editor/ # Editor layout
β βββ ui/ # UI components
βββ lib/ # Core libraries
β βββ store/ # State management
β βββ export/ # Export functionality
β βββ constants/ # Configuration
βββ hooks/ # Custom React hooks
βββ types/ # TypeScript definitions
βββ public/ # Static assets
For detailed architecture information, see ARCHITECTURE.md.
npm run dev- Start development server on http://localhost:3000npm run build- Build for productionnpm start- Start production servernpm run upload-backgrounds- Upload backgrounds to Cloudinarynpm run upload-demo-images- Upload demo images to Cloudinarynpm run upload-overlays- Upload overlays to Cloudinary
Set these in your Vercel project settings:
DATABASE_URL="postgresql://user:password@host:port/dbname?schema=public"
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your-cloud-name
CLOUDINARY_API_KEY=your-api-key
CLOUDINARY_API_SECRET=your-api-secret
CLEANUP_SECRET=your-random-secret-string
# Screenshot API URL (Optional - defaults to free Screen-Shot API)
SCREENSHOT_API_URL=https://api.screen-shot.xyzNote: SCREENSHOT_API_URL defaults to https://api.screen-shot.xyz which is a free, open-source screenshot API that requires no API key. You can deploy your own instance to Cloudflare Workers or use a different service by setting this variable.
Stage automatically caches website screenshots in Cloudinary to improve performance and reduce API calls. The cleanup process removes old cached screenshots from both Cloudinary storage and your database.
- Cache Duration: Screenshots are cached for 2 days (48 hours) by default
- Automatic Expiration: Old cache entries are automatically invalidated when accessed after expiration
- Manual Cleanup: Use the cleanup API to proactively remove old screenshots
The CLEANUP_SECRET is not provided by any service - you need to create it yourself. It's a security token that prevents unauthorized access to your cleanup endpoint.
Generate a secure random string:
# Option 1: Using OpenSSL (recommended)
openssl rand -hex 32
# Option 2: Using Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Option 3: Using Python
python3 -c "import secrets; print(secrets.token_hex(32))"
# Option 4: Online generator (use a trusted source)
# Visit: https://www.random.org/strings/Add to your .env.local file:
CLEANUP_SECRET=your-generated-secret-string-hereFor production (Vercel):
- Go to your Vercel project dashboard
- Navigate to Settings β Environment Variables
- Add
CLEANUP_SECRETwith your generated secret value - Select the appropriate environments (Production, Preview, Development)
- Click Save
Security Notes:
- Use a long, random string (at least 32 characters, 64+ recommended)
- Never commit this to version control (it's already in
.gitignore) - Use different secrets for development and production
- Treat it like a password - keep it secure
To remove screenshots older than 2 days from both Cloudinary and the database:
# Production
curl -X POST https://your-domain.com/api/cleanup-cache \
-H "Content-Type: application/json" \
-d '{"secret": "your-cleanup-secret"}'
# Local development
curl -X POST http://localhost:3000/api/cleanup-cache \
-H "Content-Type: application/json" \
-d '{"secret": "your-cleanup-secret"}'Response:
{
"success": true,
"message": "Cache cleanup completed"
}Option 1: Vercel Cron Jobs (Pro/Enterprise plans)
Add to vercel.json:
{
"crons": [
{
"path": "/api/cleanup-cache",
"schedule": "0 2 * * 0"
}
]
}Then create a cron route at app/api/cleanup-cache/cron/route.ts:
import { NextRequest, NextResponse } from 'next/server';
import { clearOldCache } from '@/lib/screenshot-cache';
export async function GET(request: NextRequest) {
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
await clearOldCache();
return NextResponse.json({ success: true });
}Option 2: External Cron Service (Free tier compatible)
Use services like:
- cron-job.org (free)
- EasyCron (free tier available)
- GitHub Actions scheduled workflows
Set up a weekly cron job that calls your cleanup endpoint.
Option 3: Manual Trigger
Run the cleanup API call manually when needed, especially when approaching Cloudinary storage limits.
The cleanup process:
- Finds all screenshot cache entries older than 2 days (default)
- Deletes the images from Cloudinary storage
- Removes the database records
- Logs the number of entries cleared
Note: Only cached screenshots are cleaned. Your uploaded backgrounds, overlays, and other assets are not affected.
Check your Cloudinary dashboard to monitor:
- Storage Usage: Total storage consumed
- Bandwidth: Monthly bandwidth usage
- Transformations: Number of image transformations
Free Tier Limits:
- Storage: 25 GB
- Bandwidth: 25 GB/month
- Transformations: 25,000/month
Recommended: Run cleanup weekly or when storage exceeds 20 GB to stay within free tier limits.
The screenshot API includes built-in rate limiting:
- Limit: 20 requests per minute per IP
- Response: 429 status with
Retry-Afterheader - Headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset
Stage is a fully in-browser canvas editor that requires no external services for core functionality.
Stage uses a hybrid canvas rendering approach with complete in-browser processing:
- Background Layer - Rendered via HTML/CSS, captured with html2canvas
- User Image Layer - Rendered via Konva Stage for precise positioning
- Overlay Layer - Text and image overlays composited on top
The export pipeline composites these layers client-side in the correct order to produce high-quality output without any server or external API calls.
- Cloudinary - Used only for screenshot caching and image optimization when configured (required for screenshot feature)
- Screen-Shot.xyz API - Free screenshot capture service (no API key required, defaults to public instance)
- Can be self-hosted on Cloudflare Workers for better control
- See Screen-Shot.xyz documentation for deployment options
For comprehensive architecture documentation, see ARCHITECTURE.md.
See CONTRIBUTING.md for development setup, project structure, and contribution guidelines.
Thanks to all our amazing contributors for their support and code!
- ARCHITECTURE.md - Detailed architecture documentation
- CONTRIBUTING.md - Contribution guidelines
- LICENSE - License information
- Export may take a few seconds for high-resolution images
- Some browsers may have limitations with large canvas operations
- Website screenshot may timeout for slow-loading websites (55s timeout)
- Screenshot feature requires database and Cloudinary configuration
- Manual cache cleanup required on free Vercel account (no cron jobs)
- Screenshot API uses free Screen-Shot.xyz service (no API key required, but rate limits may apply)
This project is licensed under the terms specified in the LICENSE file.
- Issues: GitHub Issues
- Discussions: GitHub Discussions