Back to AI Co-Drawing

Documentation

A comprehensive guide to AI-powered collaborative drawing and the implementation behind this interactive tool

1. Project Overview

The AI Co-Drawing application is an interactive collaborative drawing tool that combines human creativity with artificial intelligence. Built with React and powered by Google's Gemini 2.0 Flash model, it allows users to create drawings and have them enhanced, transformed, or evolved through AI assistance.

Core Features

  • Interactive Canvas: High-resolution drawing surface with touch and mouse support
  • AI Enhancement: Transform drawings using natural language prompts
  • Iterative Creation: Build upon AI-generated images with additional drawing
  • Real-time Processing: Live drawing with immediate visual feedback
  • Multi-modal AI: Combines image understanding with text generation capabilities

Workflow

  1. User draws on the canvas using customizable brush tools
  2. User provides a text prompt describing desired enhancements
  3. AI analyzes the drawing and prompt to generate an enhanced version
  4. Generated image becomes the new canvas background
  5. User can continue drawing on top of the AI-generated content
  6. Process repeats for iterative collaborative creation

2. AI Integration

Gemini 2.0 Flash Model

The application uses Google's latest Gemini 2.0 Flash model with native image generation capabilities. This model can understand both visual input (drawings) and textual instructions to create coherent, contextual enhancements.

Model Configuration

  • Model: gemini-2.0-flash-preview-image-generation
  • Input Modalities: Text + Image
  • Output Modalities: Text + Image
  • Max Resolution: 1024x1024 pixels

Multi-modal Processing

The AI system processes two types of input simultaneously:

AI Request Structure
1let contents: Content[] = [ 2 { 3 role: "USER", 4 parts: [{ inlineData: { data: drawingData, mimeType: "image/png" } }], 5 }, 6 { 7 role: "USER", 8 parts: [{ text: `${prompt}. Keep the same minimal line doodle style.` }], 9 }, 10]; 11 12const response = await ai.models.generateContent({ 13 model: "gemini-2.0-flash-preview-image-generation", 14 contents, 15 config: { 16 responseModalities: [Modality.TEXT, Modality.IMAGE], 17 }, 18});

Style Preservation

To maintain visual consistency, the AI is instructed to "keep the same minimal line doodle style" ensuring that enhancements feel like natural extensions of the original drawing rather than complete replacements.

3. Canvas Drawing System

Canvas Architecture

The drawing system uses HTML5 Canvas with a multi-layer approach for optimal performance and functionality.

Canvas Initialization
1const initializeCanvas = () => { 2 const canvas = canvasRef.current; 3 if (!canvas) return; 4 const ctx = canvas.getContext("2d"); 5 if (!ctx) return; 6 7 // Set high-resolution canvas size 8 canvas.width = 960; 9 canvas.height = 540; 10 11 // Fill canvas with white background 12 ctx.fillStyle = "#FFFFFF"; 13 ctx.fillRect(0, 0, canvas.width, canvas.height); 14};

Coordinate System

The canvas uses a sophisticated coordinate mapping system to handle different screen sizes and device pixel ratios:

Coordinate Mapping
1const getCoordinates = (e: React.MouseEvent | React.TouchEvent) => { 2 const canvas = canvasRef.current; 3 if (!canvas) return { x: 0, y: 0 }; 4 5 const rect = canvas.getBoundingClientRect(); 6 const scaleX = canvas.width / rect.width; 7 const scaleY = canvas.height / rect.height; 8 9 let clientX, clientY; 10 if ("touches" in e) { 11 clientX = e.touches[0]?.clientX || 0; 12 clientY = e.touches[0]?.clientY || 0; 13 } else { 14 clientX = e.clientX; 15 clientY = e.clientY; 16 } 17 18 return { 19 x: (clientX - rect.left) * scaleX, 20 y: (clientY - rect.top) * scaleY, 21 }; 22};

Drawing Engine

The drawing engine supports smooth line rendering with configurable brush properties:

Drawing Implementation
1const draw = (e: React.MouseEvent | React.TouchEvent) => { 2 if (!isDrawing) return; 3 4 const canvas = canvasRef.current; 5 if (!canvas) return; 6 const ctx = canvas.getContext("2d"); 7 if (!ctx) return; 8 9 const { x, y } = getCoordinates(e); 10 11 ctx.lineWidth = brushSize; 12 ctx.lineCap = "round"; 13 ctx.strokeStyle = penColor; 14 ctx.lineTo(x, y); 15 ctx.stroke(); 16};

4. Implementation Details

State Management

The application uses React hooks for comprehensive state management:

State Structure
1// Drawing State 2const [isDrawing, setIsDrawing] = useState(false); 3const [penColor, setPenColor] = useState("#000000"); 4const [brushSize, setBrushSize] = useState(5); 5 6// AI Generation State 7const [prompt, setPrompt] = useState(""); 8const [generatedImage, setGeneratedImage] = useState<string | null>(null); 9const [isLoading, setIsLoading] = useState(false); 10 11// Error Handling 12const [showErrorModal, setShowErrorModal] = useState(false); 13const [errorMessage, setErrorMessage] = useState("");

Background Image System

AI-generated images are seamlessly integrated as canvas backgrounds for iterative drawing:

Background Integration
1useEffect(() => { 2 if (generatedImage && canvasRef.current) { 3 const img = new window.Image(); 4 img.onload = () => { 5 backgroundImageRef.current = img; 6 drawImageToCanvas(); 7 }; 8 img.src = generatedImage; 9 } 10}, [generatedImage]); 11 12const drawImageToCanvas = () => { 13 if (!canvasRef.current || !backgroundImageRef.current) return; 14 15 const canvas = canvasRef.current; 16 const ctx = canvas.getContext("2d"); 17 if (!ctx) return; 18 19 // Fill with white background first 20 ctx.fillStyle = "#FFFFFF"; 21 ctx.fillRect(0, 0, canvas.width, canvas.height); 22 23 // Draw the background image 24 ctx.drawImage(backgroundImageRef.current, 0, 0, canvas.width, canvas.height); 25};

Touch Support

Comprehensive touch support for mobile devices with gesture prevention:

Touch Event Handling
1useEffect(() => { 2 const preventTouchDefault = (e: TouchEvent) => { 3 if (isDrawing) { 4 e.preventDefault(); 5 } 6 }; 7 8 const canvas = canvasRef.current; 9 if (canvas) { 10 canvas.addEventListener("touchstart", preventTouchDefault, { passive: false }); 11 canvas.addEventListener("touchmove", preventTouchDefault, { passive: false }); 12 } 13 14 return () => { 15 if (canvas) { 16 canvas.removeEventListener("touchstart", preventTouchDefault); 17 canvas.removeEventListener("touchmove", preventTouchDefault); 18 } 19 }; 20}, [isDrawing]);

5. User Interface

Design System

The interface follows a consistent design system with dark theme and neutral colors:

Color Palette

Background: #000000
Cards: #171717
Controls: #262626
Primary: #FFFFFF

Component Structure

  • Canvas Area: Primary drawing surface with full touch/mouse support
  • Settings Panel: Tool controls and AI configuration options
  • Drawing Tools: Brush size, color picker, and canvas management
  • AI Enhancement: Prompt input and generation controls
  • Error Modal: User-friendly error handling and display

Responsive Design

The interface adapts to different screen sizes using Tailwind CSS breakpoints:

Responsive Layout
1{/* Responsive grid layout */} 2<div className="grid grid-cols-1 xl:grid-cols-[1fr_300px] gap-6"> 3 {/* Canvas takes full width on mobile, left side on desktop */} 4 <motion.div className="bg-neutral-900 rounded-2xl border border-neutral-800 p-6"> 5 <canvas 6 className="w-full h-[400px] md:h-[500px] lg:h-[600px] bg-white cursor-crosshair touch-none" 7 /> 8 </motion.div> 9 10 {/* Settings panel stacks below on mobile, right side on desktop */} 11 <motion.div className="bg-neutral-900 rounded-2xl border border-neutral-800 p-6"> 12 {/* Settings content */} 13 </motion.div> 14</div>

6. API Integration

Environment Configuration

The application requires proper environment setup for API access:

.env.local
1# Google Generative AI API Key 2# Get your API key from: https://makersuite.google.com/app/apikey 3NEXT_PUBLIC_GOOGLE_API_KEY=your_api_key_here

Error Handling

Comprehensive error handling with user-friendly feedback:

Error Management
1function parseError(error: string) { 2 const regex = /{"error":(.*)}/gm; 3 const m = regex.exec(error); 4 try { 5 const e = m ? m[1] : error; 6 const err = JSON.parse(e); 7 return err.message || error; 8 } catch (e) { 9 return error; 10 } 11} 12 13// In the main component 14} catch (error: any) { 15 console.error("Error submitting drawing:", error); 16 setErrorMessage(error.message || "An unexpected error occurred."); 17 setShowErrorModal(true); 18} finally { 19 setIsLoading(false); 20}

Performance Considerations

  • Image Compression: Canvas exports optimized PNG data for API transmission
  • Async Processing: Non-blocking AI generation with loading states
  • Memory Management: Proper cleanup of canvas contexts and event listeners
  • Error Recovery: Graceful handling of API failures with retry options
  • Client-side Rendering: All drawing operations run locally for immediate feedback

Security

API keys are handled securely through environment variables with the NEXT_PUBLIC_ prefix for client-side access. The application includes input validation and sanitization to prevent malicious prompts or canvas manipulations.

This documentation covers the technical implementation of the AI Co-Drawing application. For questions about usage or to contribute improvements, explore the source code or reach out directly.