My personal website hosted using ReactJS, Supabase, & Super.so that utilizes a minimalist, full-screen web canvas where visitors can draw, save, and submit artwork. Built as an interactive engagement feature for my personal portfolio, complete with a custom analytics system and admin dashboard for tracking engagement and moderating submissions.
Home Page: ykabusalah.me
Drawing Canvas: draw.ykabusalah.me
The project uses a hybrid architecture that combines a no-code platform with custom React development:
- Main Portfolio (ykabusalah.me): Hosted on Super.so, a Notion-based website builder. This allows for easy content updates and management without redeploying code.
- Drawing App (draw.ykabusalah.me): A separate React application deployed on its own subdomain, handling all the interactive canvas functionality, submissions, and admin features.
- Cross-Domain Connection: A lightweight JavaScript snippet embedded in Super.so's custom code footer tracks visitor activity and passes a unique visitor ID to the React app via URL parameters, enabling unified analytics across both platforms.
This approach keeps content management simple while allowing full control over the interactive drawing experience and custom analytics.
- Features
- Tech Stack
- File Structure
- Routes
- Database Schema
- Getting Started
- Keyboard Shortcuts
- Admin Features
- Custom Analytics System
- Environment Variables
- Deployment
- License
- Full-viewport, edge-to-edge drawing area
- Dynamic resizing that preserves existing strokes on window resize
- Adjustable brush size (1-20px) via custom triangle-shaped slider
- True eraser using canvas
globalCompositeOperation: 'destination-out' - Complete undo/redo history with full state snapshots
- Clear canvas functionality with history reset
- Mobile device detection with desktop guidance
- Minimum window size enforcement (800x600px)
- Clean, minimalist landing page with live counter of featured drawings
- Right-aligned sticky vertical toolbar with Lucide React icons
- Press-state feedback with button inversion and active tool highlighting
- Animated call-to-action button with pencil icon rotation on hover
- Pulsing indicator on the featured count badge
- Save modal with name entry and terms agreement
- Image upload to Supabase storage with RLS-protected insert policy
- Confetti celebration feedback on successful submission
- Real-time status tracking (pending, approved, rejected)
| Category | Technology |
|---|---|
| Frontend | React 18, TailwindCSS, Lucide React |
| Effects | canvas-confetti |
| Backend/Database | Supabase (PostgreSQL) |
| Storage | Supabase Storage (drawing-bucket) |
| Authentication | Supabase Auth |
| Analytics | Custom-built tracking system |
| Hosting | Custom subdomain (draw.ykabusalah.me) |
| Version Control | Git/GitHub |
src/
├── index.js # Route setup (React Router)
├── index.css # Global styles + custom slider CSS
├── analytics.js # Custom analytics tracking
├── App.jsx # Main drawing canvas
├── Info.jsx # Landing/intro page
├── ThankYou.jsx # Post-submission confirmation
├── ModerationPanel.jsx # Admin panel for approving drawings
├── Statistics.jsx # Analytics dashboard (admin only)
| Path | Component | Description |
|---|---|---|
/ |
Redirect | Redirects to /info |
/info |
Info | Welcome page explaining the concept |
/draw |
App | Main drawing canvas |
/thank-you |
ThankYou | Shown after successful submission |
/moderate |
ModerationPanel | Admin login + approval interface |
/stats |
Statistics | Analytics dashboard (requires auth) |
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| name | text | Submitter's name (optional) |
| image_url | text | Public URL to stored image |
| status | text | 'pending', 'approved', or 'rejected' |
| created_at | timestamp | Submission time |
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| session_id | text | Unique per browser session |
| event_name | text | Event type (page_view, tool_change, etc.) |
| event_data | jsonb | Event-specific data + visitor_id |
| device_type | text | 'desktop', 'mobile', or 'tablet' |
| screen_width | int | Browser width |
| screen_height | int | Browser height |
| created_at | timestamp | Event time |
- Node.js (v16 or higher)
- npm or yarn
- Supabase account and project
-
Clone the repository:
git clone https://github.com/yourusername/drawing-canvas.git cd drawing-canvas -
Install dependencies:
npm install
-
Create a
.envfile in the root directory:REACT_APP_SUPABASE_URL=your_supabase_url REACT_APP_SUPABASE_ANON_KEY=your_supabase_anon_key
-
Set up Supabase:
- Create a
drawingstable with the schema above - Create an
analyticstable with the schema above - Create a storage bucket named
drawing-bucket - Configure RLS policies (anonymous inserts for analytics, authenticated reads)
- Create a
-
Start the development server:
npm start
| Key | Action |
|---|---|
P |
Switch to Pencil |
E |
Switch to Eraser |
Ctrl+Z |
Undo |
Ctrl+Y / Ctrl+Shift+Z |
Redo |
[ |
Decrease brush size |
] |
Increase brush size |
Ctrl+S |
Open save modal |
Delete / Backspace |
Clear canvas |
Escape |
Close modals |
- Supabase Auth login required
- Card-based grid layout for pending submissions
- Click-to-enlarge image preview in modal overlay
- One-click approve/reject actions with real-time updates
- Stats cards showing pending, approved, and rejected counts
- Navigation link to Statistics dashboard
Built a custom analytics system from scratch to replace Google Analytics, providing more relevant metrics for tracking user behavior through the drawing funnel.
The system consists of three main components:
1. analytics.js - Event Tracking Utility
A lightweight tracking module that sends events directly to a Supabase analytics table. Each event includes:
session_id: Generated using timestamp + random string, stored insessionStorage(unique per browser tab)visitor_id: Generated similarly but stored inlocalStorage(persists across sessions for returning visitor tracking)event_name: The type of action being trackedevent_data: JSON object with event-specific detailsdevice_type: Detected via user agent parsing (desktop/mobile/tablet)screen_width/screen_height: Viewport dimensions at time of event
Exported tracking functions:
trackPageView(page)
trackDrawingStart(tool, brushSize)
trackToolChange(tool, previousTool)
trackBrushSize(size)
trackUndo()
trackRedo()
trackClear()
trackSubmitStart()
trackSubmitSuccess(hasName)
trackSubmitError(error)
trackExit(confirmed)
trackModalClose()2. Cross-Domain Tracking (Super.so Integration)
Since the main portfolio (ykabusalah.me) is hosted on Super.so and the drawing app (draw.ykabusalah.me) is a separate React app, a lightweight tracking snippet is embedded in Super.so's custom code footer:
- Tracks
page_viewevents on the home page - Tracks
draw_link_clickwhen visitors click the draw button - Passes the
visitor_idto the drawing app via URL parameter (?vid=...) - The drawing app reads the
vidparameter, stores it in localStorage, and cleans the URL usingwindow.history.replaceState()
This enables tracking the complete user journey from portfolio visit through drawing submission.
3. Statistics.jsx - Analytics Dashboard
An authenticated admin dashboard that queries the analytics table and calculates metrics using time-range filters (7/30/90/365 days or all-time).
Traffic and Conversion:
- Full conversion funnel: Home -> Draw Click -> Info -> Draw -> Submit
- Step-by-step conversion rates between each stage
- Direct vs referred visitor breakdown (from home page vs direct URL)
- Click-through rate from home page to drawing app
- Full funnel completion rate
Drop-off Analysis:
- Home page bounce rate
- Info page drop-off (clicked draw but left on info)
- Info page bounce rate
- Modal abandonment rate (opened save modal but didn't submit)
Drawing Behavior:
- Undo/redo frequency and average per session
- Most popular brush sizes
- Brush size change frequency
- Average time on draw page before submitting
- Canvas clear frequency
- Drawing completion rate
Visitor Analytics:
- Unique sessions and page views
- Returning visitor rate (tracked via persistent visitor_id in localStorage)
- Total visitor count over time
Activity Patterns:
- Activity heatmap by hour of day
- Activity breakdown by day of week
- Monthly activity trends
Submission Stats:
- Approval rate percentage
- Approved/rejected/pending counts
- Exit button behavior (confirmed vs cancelled)
- More granular control over exactly what events are tracked
- Direct access to raw data in Supabase for custom queries
- No third-party dependencies or cookie consent requirements
- Metrics tailored specifically to the drawing app funnel
- Cross-domain tracking without complex GA configuration
REACT_APP_SUPABASE_URL=your_supabase_url
REACT_APP_SUPABASE_ANON_KEY=your_anon_keyanalytics: Anonymous inserts allowed, authenticated reads onlydrawings: Configure based on your needs (public read for approved, authenticated for all)
Currently deployed at draw.ykabusalah.me as a subdomain connected to the main portfolio site hosted on Super.so.
npm run buildThis project is open source and available under the MIT License.
Created by Yousef Abu-Salah