DEV Community

Cover image for Building a multiplayer TriviaSnake game with Amazon Q Developer!

Building a multiplayer TriviaSnake game with Amazon Q Developer!

Chat with My Digital Twin: How I Built an AI Version of Myself You Can Interview

AWS Builder Challenge #2 submission showcasing what's possible when you push beyond the requirements


The Strategic Skip: Why I Started from Day 4

As an experienced software engineer, I'll be honest—I almost skipped the AWS Builder Challenge entirely. "Build a personal website" seemed like another "Hello World" tutorial. But then I read the fine print: 7 days, Free Tier only, make it your own.

That's when it clicked. This wasn't about following instructions—it was about taking constraints and building something remarkable within them.

The Complete 7-Day Journey

The full challenge provides a comprehensive introduction to cloud development:

  • Day 1: Secure Foundation - AWS Free Tier setup, MFA, IAM users, budget alerts
  • Day 2: Private Cloud Storage - S3 bucket creation with security best practices
  • Day 3: Web Content - HTML/CSS development, customizing templates, secure uploads
  • Day 4: Global Distribution - CloudFront CDN, solving the distance problem
  • Day 5: CI/CD Pipeline - GitHub integration, Amplify deployment automation
  • Day 6: Interactive Features - Serverless contact forms with Lambda + SES
  • Day 7: Community Showcase - Writing and sharing your experience

I strategically skipped Days 1-4 since I already had AWS experience with these services. Days 1-3 covered account setup, S3, and static HTML - all foundational but familiar territory. Day 4's CloudFront was also something I'd implemented many times through CDK for other projects.

Instead, I jumped straight to Day 5 where things got interesting: modern deployment pipelines with Amplify.

For beginners, the first four days provide excellent foundations in AWS security, storage, web development, and global distribution. Don't skip them unless you're already comfortable with these concepts.

The Real Challenge: From Static to Interactive

Day 5: CI/CD with GitHub and Amplify - The New Territory

This is where the challenge got practical. Setting up GitHub integration with AWS Amplify through the console taught me something valuable: modern deployment pipelines should be boring.

The magic happens when you push code and everything just... works:

  • GitHub repository integration through the Amplify console
  • Automatic deployments triggered by every commit to main branch
  • Zero configuration - Amplify auto-detected my static HTML site
  • Built-in CDN - Same CloudFront performance, but managed automatically
  • HTTPS by default - No certificate management needed

The setup process was surprisingly straightforward:

  1. "Deploy an app" in Amplify console
  2. Connect GitHub repository with OAuth authorization
  3. Auto-detect build settings (no amplify.yml needed for static sites)
  4. Deploy and get instant HTTPS URL

Within minutes, I had the same global performance as my manual CloudFront setup, but with automatic deployments and a cleaner workflow.

Day 6: Interactive Contact Form - The Reality Check

Building a serverless contact form sounds simple until you consider:

  • Input validation and sanitization
  • Rate limiting and spam protection
  • Email delivery reliability
  • Error handling and user feedback
  • Accessibility compliance

I used AWS Lambda + SES with the exact code provided in the challenge. Rather than reinventing the wheel, I followed the tutorial's implementation:

// Exact Lambda function code from Day 6 challenge
import json
import boto3

def lambda_handler(event, context):
    try:
        # Parse the form data from the request
        body = json.loads(event['body'])
        name = body.get('name', '')
        email = body.get('email', '')
        message = body.get('message', '')

        # Validate that all required fields are present
        if not all([name, email, message]):
            return {
                'statusCode': 400,
                'body': json.dumps({'error': 'Missing required fields'})
            }

        # Format the notification message
        notification_message = f"""Contact Form Submission

Name: {name}
Email: {email}

Message:
{message}"""

        # Send notification via SNS
        sns = boto3.client('sns')
        sns.publish(
            TopicArn='arn:aws:sns:us-east-1:123456789012:contact-form-notifications',
            Subject=f'Contact Form: {name}',
            Message=notification_message
        )

        # Return success response
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Message sent successfully!'})
        }

    except Exception as e:
        # Return error response if anything goes wrong
        return {
            'statusCode': 500,
            'body': json.dumps({'error': 'Failed to send message'})
        }
Enter fullscreen mode Exit fullscreen mode

The beauty was in its simplicity - the challenge provided a working solution that I could deploy immediately.

Beyond the Challenge: Building a Production Portfolio

Before diving into the AI innovation, let me showcase what I actually built as the foundation. While the challenge provided basic HTML templates, I went completely custom to create a production-ready portfolio that demonstrates serious frontend skills.

The Kiro Advantage: Spec-Driven Development

Here's where I have to give credit where it's due - Kiro IDE's spec-driven workflow was instrumental in transforming the basic AWS challenge template into something production-ready.

Instead of just "customizing" the provided HTML template, I used Kiro to architect a complete system:

What Kiro Generated:

  • 7 detailed user stories with acceptance criteria (from "understand who Karthik is" to "chat with AI version")
  • Complete design system including dark theme color palettes, typography scales, and component specifications
  • 17-step implementation plan systematically transforming basic template to production portfolio
  • Data architecture with structured models for experience, projects, skills, and certifications

From the actual Kiro requirements spec:

"This project involves creating an elegant, minimalist personal website for Karthik Subramanian, a Principal-level Software Engineer who excels in backend development but can also build beautiful frontend web applications. The website should be streamlined, to-the-point, and use a dark theme with scroll-based discovery instead of traditional navigation."

The Spec-Driven Transformation:

Rather than ad-hoc customization, Kiro mapped out systematic requirements like:

  • Requirement 3: "I want the website to demonstrate Karthik's frontend development skills through elegant design and smooth interactions"
  • Requirement 7: "I want to chat with an AI version of Karthik, so that I can have interactive conversations about his experience"

Each requirement came with detailed acceptance criteria and was mapped to specific implementation tasks. This meant every design decision was intentional and measurable.

The Result: What could have been a simple template customization became a comprehensive design system with proper color schemes, responsive breakpoints, accessibility standards, and even AI integration specifications.

This is the power of specification-driven development - instead of "making it look nice," I had a clear architectural plan that transformed a basic challenge template into a sophisticated portfolio platform.

Professional Design Architecture

Instead of using the basic template, I built a fully custom dark-theme portfolio with sophisticated visual design:

  • Cohesive dark color scheme using CSS custom properties for maintainability
  • Professional typography with Inter and JetBrains Mono fonts
  • Gradient backgrounds and subtle patterns throughout
  • Comprehensive hover animations and micro-interactions
  • Custom scrollbar styling for consistent branding

Accessibility-First Implementation

This wasn't just about making it "look good" - I implemented WCAG AA compliance from the ground up:

// Real accessibility features from my accessibility-enhancements.js
class AccessibilityEnhancer {
    setupKeyboardDetection() {
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Tab') {
                this.isKeyboardUser = true;
                document.body.classList.add('keyboard-navigation-active');
            }
        });
    }

    updateCurrentSection(section) {
        if (this.currentSection) {
            this.currentSection.removeAttribute('aria-current');
        }
        this.currentSection = section;
        section.setAttribute('aria-current', 'location');

        const sectionName = this.getSectionName(section);
        if (sectionName && this.isKeyboardUser) {
            this.announce(`Now viewing ${sectionName} section`);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Features implemented:

  • Skip navigation links with proper focus management
  • Screen reader announcements via ARIA live regions
  • Keyboard navigation detection and enhanced focus styles
  • Section tracking with aria-current for screen readers
  • Keyboard shortcuts (Alt + 1-6 for sections, Alt + H for home)
  • High contrast mode support for Windows accessibility
  • Reduced motion preferences respected throughout

Mobile-First Responsive Design

Built with mobile-first methodology and comprehensive breakpoint strategy:

/* Mobile-first responsive implementation from main.css */
@media (max-width: 640px) {
    .text-5xl { font-size: 2.25rem !important; }
    .py-20 { padding-top: 3rem !important; padding-bottom: 3rem !important; }

    /* Touch-friendly targets */
    a, button, [role="button"] {
        min-height: 48px;
        min-width: 48px;
        padding: 0.75rem;
    }
}

@media (min-width: 1025px) {
    .text-5xl { font-size: 3.5rem; }
    .hero-content { max-width: 1024px; }
}
Enter fullscreen mode Exit fullscreen mode

Responsive features implemented:

  • Dynamic responsive classes - JavaScript adds mobile/tablet/desktop/large-desktop classes
  • Touch device detection with appropriate UI adjustments
  • Progressive typography scaling across all breakpoints
  • Content layout optimization for different screen sizes
  • Touch-friendly interface elements (minimum 44px targets)

Performance Optimizations

Every optimization implemented for Free Tier efficiency:

/* Performance optimizations from main.css */
.animate-on-scroll {
    will-change: transform, opacity;
    backface-visibility: hidden;
    perspective: 1000px;
}

.animation-complete {
    will-change: auto; /* Remove after animation for performance */
}

img {
    max-width: 100%;
    height: auto;
    aspect-ratio: attr(width) / attr(height); /* Prevent layout shift */
}
Enter fullscreen mode Exit fullscreen mode

Performance features:

  • Image lazy loading with Intersection Observer
  • Animation performance optimizations using will-change and backface-visibility
  • Reduced animations on mobile to preserve battery life
  • CSS containment for better rendering performance
  • Font loading optimization with preconnect hints
  • Smooth scrolling with proper scroll-padding

Advanced UX Features

Interactive elements that enhance the user experience:

// Real scroll progress indicator from animations.js
function initScrollProgressIndicator() {
    const progressBar = document.createElement('div');
    progressBar.className = 'fixed top-0 left-0 h-1 bg-blue-400 z-50';
    progressBar.setAttribute('role', 'progressbar');
    progressBar.setAttribute('aria-label', 'Page scroll progress');

    function updateScrollProgress() {
        const scrollTop = window.scrollY;
        const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
        const scrollPercent = Math.min(100, Math.max(0, (scrollTop / scrollHeight) * 100));

        progressBar.style.width = `${scrollPercent}%`;
        progressBar.setAttribute('aria-valuenow', Math.round(scrollPercent));
    }

    window.addEventListener('scroll', requestAnimationFrame(updateScrollProgress));
}
Enter fullscreen mode Exit fullscreen mode

Features implemented:

  • Scroll progress indicator with ARIA progressbar role for accessibility
  • Word rotation animation in hero tagline with layout shift prevention
  • Intersection Observer animations with staggered delays and performance cleanup
  • Interactive skills switching with keyboard navigation and ARIA states
  • Timeline animations for experience section with hover effects
  • Email template functionality - right-click on email links for quick templates
  • Touch device optimization with appropriate target sizes and feedback
  • Loading states and visual feedback for all interactive elements

The Unique Twist: Local AI Chat Integration

Here's where I went completely off-script. The challenge wanted a personal website, but I thought: What if visitors could actually chat with an AI version of me?

Why Chrome's Built-in AI?

Most AI implementations require:

  • External API calls (costs money)
  • Server-side processing (not Free Tier friendly)
  • Complex authentication (user friction)

Chrome's Gemini Nano runs entirely in the browser. Zero cost. Zero servers. Maximum privacy.

// Real AI detection code from my ai-manager.js
async detectAICapabilities() {
    this.log('🔍 Detecting AI capabilities...');

    try {
        if (typeof LanguageModel === 'undefined') {
            this.log('❌ LanguageModel API not available');
            this.logSetupInstructions();
            return false;
        }

        this.log('✅ LanguageModel API found');

        // Check availability
        const availability = await LanguageModel.availability();
        this.log('📊 AI availability:', availability);

        // Store capabilities
        this.capabilities = { available: availability };

        // Set appropriate state
        switch (availability) {
            case 'available':
                this.setState('available');
                return true;
            case 'downloadable':
                this.setState('downloadable');
                return true;
            case 'downloading':
                this.setState('downloading');
                this.startAvailabilityPolling();
                return true;
            case 'unavailable':
                this.setState('unavailable');
                return false;
            default:
                this.log('❌ Unknown availability status:', availability);
                this.setState('unavailable');
                return false;
        }

    } catch (error) {
        this.log('❌ Error detecting AI capabilities:', error);
        return false;
    }
}
Enter fullscreen mode Exit fullscreen mode

The Technical Implementation Challenge

The real challenge was managing different AI states and providing appropriate UI feedback:

// Real state management from ai-manager.js
updateTeaserUI() {
    const teaserText = document.getElementById('ai-teaser-inline-text');
    const detectionLoading = document.getElementById('ai-detection-loading');
    const downloadingIndicator = document.getElementById('ai-downloading-indicator');
    const downloadBtn = document.getElementById('ai-download-btn');
    const chatReadyBtn = document.getElementById('ai-chat-ready-btn');

    if (!teaserText) return;

    // Update text message
    teaserText.textContent = this.getStateMessage();

    // Update indicators based on state
    this.hideElement(detectionLoading);
    this.hideElement(downloadingIndicator);
    this.hideElement(downloadBtn);
    this.hideElement(chatReadyBtn);

    switch (this.state) {
        case 'checking':
            this.showElement(detectionLoading);
            break;
        case 'downloadable':
            this.showElement(downloadBtn);
            break;
        case 'downloading':
            this.showElement(downloadingIndicator);
            break;
        case 'available':
            this.showElement(chatReadyBtn);
            break;
    }
}
Enter fullscreen mode Exit fullscreen mode

The hardest part was getting the prompt to be both accurate and comprehensive. My solution: dynamic prompts built from real profile data:

// Real dynamic prompt system from ai-chat-interface.js
buildSystemPrompt() {
    const now = new Date();
    const currentDateTime = now.toLocaleString('en-US', {
        weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
        hour: '2-digit', minute: '2-digit', timeZoneName: 'short'
    });

    // Get data from global data.js variables - this is the key insight
    const personalData = typeof personalInfo !== 'undefined' ? personalInfo : {};
    const experienceData = typeof experience !== 'undefined' ? experience : [];
    const projectsData = typeof projects !== 'undefined' ? projects : [];
    const skillsData = typeof skills !== 'undefined' ? skills : {};

    return `You are Karthik Subramanian, responding in first person as yourself.

CURRENT CONTEXT:
- Current date and time: ${currentDateTime}
- You are responding to someone visiting your personal portfolio website

PERSONAL INFORMATION:
- Name: ${personalData.name || 'Karthik Subramanian'}
- Title: ${personalData.title || 'Senior Software Engineering Manager'}
- Current Company: Scholastic Inc.
- Bio: ${personalData.bio || ''}

PROFESSIONAL EXPERIENCE:
${experienceData.map(job => 
    `- ${job.position} at ${job.company} (${job.duration})
  ${job.description}
  Key achievements: ${job.achievements?.join(', ') || ''}
  Technologies: ${job.technologies?.join(', ') || ''}`
).join('\n\n')}

FEATURED PROJECTS:
${projectsData.filter(p => p.featured).map(project => 
    `- ${project.title}: ${project.description}
  Technologies: ${project.technologies?.join(', ') || ''}
  ${project.liveUrl ? `Live: ${project.liveUrl}` : ''}
  ${project.githubUrl ? `GitHub: ${project.githubUrl}` : ''}`
).join('\n\n')}

IMPORTANT GUIDELINES:
- Always respond in first person as Karthik
- Be conversational and personable, but professional
- Only provide information that is included in this profile
- If asked about something not in your profile, politely redirect
- Remember: You are Karthik having a conversation about your professional experience`;
}
Enter fullscreen mode Exit fullscreen mode

Instead of hardcoded text, the AI gets live data from my profile - experience, projects, skills, even the current date for context. This means when I update my portfolio data, the AI automatically gets the latest information. Does it work well? You tell me ;)

Graceful Degradation Strategy

The AI chat is an enhancement, not a requirement. For unsupported browsers, I implemented keyword-based responses:

// Real fallback response logic from ai-chat-interface.js
generateResponse(userMessage) {
    const message = userMessage.toLowerCase();

    if (message.includes('experience') || message.includes('work') || message.includes('job')) {
        return "I'm currently a Senior Software Engineering Manager at Scholastic Inc., where I lead technical teams and drive software architecture decisions. I have over 13 years of experience in software development, from full-stack development to technical leadership roles.";
    }

    if (message.includes('skills') || message.includes('technology') || message.includes('tech')) {
        return "My core expertise spans backend systems (Node.js, Python, Java), cloud architecture (AWS, serverless), and frontend development (JavaScript, React, CSS). I specialize in building scalable systems and leading technical teams.";
    }

    if (message.includes('projects') || message.includes('portfolio')) {
        return "I've built several interesting projects including a multiplayer TriviaSnake game using AWS and WebSockets, comprehensive AWS serverless tutorials, and this interactive portfolio website. You can check out my GitHub for more projects!";
    }

    // Default response
    return "That's a great question! I'd be happy to share more about my experience in software engineering, technical leadership, or any of my projects. Feel free to ask about my work at Scholastic, my AWS expertise, or anything else you'd like to know!";
}
Enter fullscreen mode Exit fullscreen mode

The Progressive Enhancement Approach:

  • Site works perfectly without AI (traditional contact form and static content)
  • AI features only activate when supported browser is detected
  • Graceful fallback to keyword-based responses when AI session creation fails
  • Clear messaging about browser requirements without breaking the experience

Production Polish: The Details That Matter

Accessibility First

The challenge didn't mention accessibility, but it should be table stakes:

<!-- Semantic HTML structure -->
<main id="main-content" tabindex="-1">
  <section aria-labelledby="hero-heading" role="banner">
    <h1 id="hero-heading">Karthik Subramanian</h1>
    <p id="hero-description">Backend systems architect...</p>
  </section>
</main>

<!-- Skip navigation for keyboard users -->
<nav aria-label="Skip navigation links" class="sr-only focus-within:not-sr-only">
  <a href="#main-content" class="skip-link">Skip to main content</a>
</nav>

<!-- Live regions for dynamic content -->
<div id="live-region" aria-live="polite" aria-atomic="true" class="sr-only"></div>
Enter fullscreen mode Exit fullscreen mode

Performance Optimizations

Free Tier means every byte counts:

  • Image optimization with proper formats and sizes
  • Font subsetting to load only used characters
  • CSS purging with Tailwind's production build
  • JavaScript lazy loading for non-critical features
  • Resource hints for better loading performance
<!-- Preconnect to external domains -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- Optimized font loading -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">

<!-- Critical CSS inlined, non-critical deferred -->
<style>/* Critical CSS here */</style>
<link rel="preload" href="styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
Enter fullscreen mode Exit fullscreen mode

Responsive Design Philosophy

Mobile-first, but desktop-optimized:

/* Base mobile styles */
.hero-content {
  padding: 2rem 1rem;
  text-align: center;
}

/* Progressive enhancement for larger screens */
@media (min-width: 768px) {
  .hero-content {
    padding: 4rem 2rem;
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    text-align: left;
  }
}

@media (min-width: 1024px) {
  .hero-content {
    padding: 6rem 3rem;
    max-width: 1200px;
    margin: 0 auto;
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Takeaways for Fellow Experienced Devs

1. Constraints Spark Creativity

The Free Tier limitation forced innovative solutions. Local AI processing, optimized images, efficient caching—creativity born from constraints.

2. Skip Smart, Not Blindly

I skipped the basics I knew but thoroughly explored areas that were new. Day 4's CloudFront configuration taught me more than expected.

3. User Experience > Technical Prowess

The AI chat is cool, but the site works perfectly without it. Progressive enhancement should be our default approach.

4. Modern Web Standards Matter

Accessibility, performance, and responsive design aren't optional anymore. Build them in from day one.

5. Ship Fast, Iterate Faster

The 7-day constraint prevented perfectionism. Sometimes "good enough to ship" is exactly what you need.

The Results

The final website features:

  • Sub-2-second load times globally via CloudFront
  • 🤖 Local AI chat with personality-matched responses
  • WCAG AA compliance with full keyboard navigation
  • 📱 Responsive design that works on any device
  • 🔒 Security headers and proper HTTPS enforcement
  • 📈 Performance scores of 90 on all Lighthouse metrics

Live site: https://main.d10rgge0ny3m7sv.amplifyapp.com/

Screenshot needed: Full homepage view showing the hero section with profile photo, AI teaser card, and overall dark theme design


Visual Showcase

Hero Section with AI Integration
Hero Section with AI Teaser
The hero section showcasing the professional dark theme design with profile photo, inspiring quote, and the innovative "Wanna know more about me?" AI teaser card that detects Chrome AI capabilities

AI Superpowers Section
AI Superpowers Cards

The "AI Superpowers" section displaying four professionally designed cards: AI Whisperer (Cline, GitHub Copilot, Claude), Context Curator (AWS Bedrock, PostgreSQL), Local AI Pioneer (Chrome AI, Gemini Nano), and AI Workflow Orchestrator (Step Functions, Bedrock)

Technical Skills Showcase
Digital Toolbox
The "Digital Toolbox" section highlighting programming languages, frameworks, and extensive AWS services expertise with color-coded skill badges demonstrating production-ready technical capabilities

Responsive Design & Accessibility
The site implements mobile-first responsive design with WCAG AA compliance, keyboard navigation support, skip links, and semantic HTML structure throughout all sections

Final Thoughts

The AWS Builder Challenge succeeded in ways the organizers probably didn't expect. It wasn't about learning AWS basics—it was about rediscovering the joy of building within constraints.

For experienced developers considering similar challenges: don't skip them entirely. Instead, use them as opportunities to explore adjacent technologies, implement best practices you've been meaning to try, or push the boundaries of what's possible within the given constraints.

The best part? Everything runs on AWS Free Tier. Zero ongoing costs for a production-ready personal site with AI capabilities.

What constraints will spark your next creative breakthrough?


About the Author: Karthik Subramanian is a Principal Software Engineer and AWS Community Builder with 10+ years of experience building scalable systems. When not writing code, he's chasing twin toddlers, perfecting his espresso technique, or exploring Waterloo with his drone.

Technical Stack: AWS (CloudFront, Amplify, Lambda, SES), Chrome AI, Tailwind CSS, Vanilla JavaScript
Source Code: Available on GitHub

cloud-launch-challenge-1 #aws #ai #webdevelopment #accessibility

Top comments (0)