Building a Professional Embedded Systems Portfolio Website with Advanced Animations
Learn to build a modern portfolio website with 12 advanced animation features including PCB traces, particle systems, terminal simulation, and AWS deployment
Overview
This comprehensive tutorial will guide you through building a professional portfolio website specifically designed for embedded systems engineers, featuring 12 advanced animation systems including animated PCB traces, particle effects, matrix rain, oscilloscope waveforms, and an interactive terminal simulation. You’ll also learn complete AWS EC2 deployment with Nginx and SSL configuration.
What You’ll Build:
- Modern dark-themed portfolio with red accent (#ef4444)
- 12 unique animation features (PCB traces, particles, matrix rain, typing effects, terminal, waveforms, LEDs, circuit nodes, glitch effects, stat counters)
- Fully responsive design for all devices
- Production-ready deployment on AWS EC2
- Custom domain with SSL certificate (Let’s Encrypt)
Live Example: profile.sarcasticrobo.com
Prerequisites
Required Software
- Code Editor: VS Code, Sublime Text, or any modern editor
- Web Browser: Chrome, Firefox, or Safari (latest version)
- Local Server: Python 3 (for testing) or Node.js
- Git: For version control
- AWS Account: For deployment (free tier eligible)
- Domain Name: Optional but recommended (e.g., sarcasticrobo.com)
Required Knowledge
- HTML5: Semantic markup, forms, SVG basics
- CSS3: Flexbox, Grid, animations, transforms, keyframes
- JavaScript ES6+: DOM manipulation, Canvas API, event handling, classes
- Basic Linux: SSH, file permissions, package management
- Nginx: Basic web server configuration
- Git: Basic version control commands
Optional Tools
- SSH Client: PuTTY (Windows), built-in terminal (Mac/Linux)
- SCP/SFTP Client: FileZilla, WinSCP for file transfers
- Image Editor: For optimizing profile and project images
Step 1: Project Setup and Structure
Create Project Directory
mkdir Personal && cd Personal
mkdir -p css js assets/images
touch index.html css/style.css
touch js/main.js js/animations.js js/circuit-nodes.js js/effects.js
Project Structure
Personal/
├── index.html # Main HTML with all sections
├── css/
│ └── style.css # Complete styling + animations
├── js/
│ ├── animations.js # PCB traces, particles, matrix rain, typing, waveforms, LEDs
│ ├── circuit-nodes.js # Skills connection visualization
│ ├── effects.js # Stat counters, glitch effects, scan line
│ └── main.js # Navigation, scroll, form handling
├── assets/
│ └── images/
│ ├── profilephoto.jpg # Your profile image
│ └── README.md # Image documentation
└── README.md # Project documentation
Initialize Git Repository
git init
echo "# Professional Portfolio Website" > README.md
git add .
git commit -m "Initial project structure"
Step 2: Building the HTML Foundation
Create Basic HTML Structure
Create index.html with semantic sections:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deepak Singhal - Embedded Systems Engineer</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- Navigation -->
<nav class="navbar">
<div class="container">
<a href="#" class="logo">DS</a>
<ul class="nav-links">
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#projects">Projects</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div>
</nav>
<!-- Hero Section with Animations -->
<section id="home" class="hero">
<div class="hero-content">
<h1 class="glitch-text">Deepak Singhal</h1>
<p class="typing-text"></p>
<div class="hero-buttons">
<a href="#contact" class="btn btn-primary">Get In Touch</a>
<a href="assets/Resume_Deepak_Singhal.pdf" class="btn btn-secondary" download>Download Resume</a>
</div>
</div>
<canvas id="particle-canvas"></canvas>
<canvas id="matrix-canvas"></canvas>
</section>
<!-- About Section -->
<section id="about" class="section">
<div class="container">
<h2 class="section-title glitch-text">About Me</h2>
<div class="about-content">
<div class="profile-image">
<img src="assets/images/profilephoto.jpg" alt="Deepak Singhal">
</div>
<div class="about-text">
<p>Passionate Embedded Systems Engineer with expertise in IoT, RTOS, and firmware development...</p>
<!-- Stats with Animated Counters -->
<div class="stats">
<div class="stat-item">
<span class="stat-number" data-target="7">0</span>
<span class="stat-label">Years Experience</span>
</div>
<div class="stat-item">
<span class="stat-number" data-target="10">0</span>
<span class="stat-label">Projects Completed</span>
</div>
<div class="stat-item">
<span class="stat-number" data-target="5">0</span>
<span class="stat-label">Industries Served</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Skills Section with Circuit Nodes -->
<section id="skills" class="section">
<div class="container">
<h2 class="section-title glitch-text">Technical Skills</h2>
<canvas id="circuit-canvas"></canvas>
<div class="skills-grid">
<div class="skill-category">
<h3>Embedded Systems</h3>
<div class="skill-tags">
<span class="skill-tag" data-skill="C/C++">C/C++</span>
<span class="skill-tag" data-skill="ARM Cortex">ARM Cortex</span>
<span class="skill-tag" data-skill="ESP32">ESP32</span>
</div>
</div>
<!-- More categories... -->
</div>
</div>
</section>
<!-- Projects Section -->
<section id="projects" class="section">
<div class="container">
<h2 class="section-title glitch-text">Featured Projects</h2>
<div class="projects-grid">
<div class="project-card">
<h3 class="glitch-text">IoT Sensor Network</h3>
<p>Distributed sensor network with MQTT and edge computing...</p>
<div class="project-tags">
<span>ESP32</span>
<span>MQTT</span>
<span>AWS IoT</span>
</div>
<div class="project-links">
<a href="#" class="btn-link">View Demo</a>
<a href="#" class="btn-link">GitHub</a>
</div>
</div>
<!-- More projects... -->
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="section">
<div class="container">
<h2 class="section-title glitch-text">Get In Touch</h2>
<form id="contact-form" action="https://formsubmit.co/deepaksinghal1995@gmail.com" method="POST">
<input type="hidden" name="_subject" value="New message from Portfolio Website">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<input type="text" name="subject" placeholder="Subject" required>
<textarea name="message" placeholder="Your Message" rows="5" required></textarea>
<button type="submit" class="btn btn-primary">Send Message</button>
</form>
</div>
</section>
<!-- Embedded Terminal Simulation -->
<div id="terminal" class="terminal">
<div class="terminal-header">
<span>system@embedded-portfolio:~$</span>
<div class="terminal-controls">
<button class="terminal-btn minimize">_</button>
<button class="terminal-btn close">×</button>
</div>
</div>
<div class="terminal-body">
<div id="terminal-output"></div>
<div class="terminal-input-line">
<span class="terminal-prompt">$ </span>
<input type="text" id="terminal-input" placeholder="Type 'help' for commands">
</div>
</div>
</div>
<!-- JavaScript Files -->
<script src="js/animations.js"></script>
<script src="js/circuit-nodes.js"></script>
<script src="js/effects.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Key HTML Features Explained
- Semantic Structure: Uses proper HTML5 sections (nav, section, footer)
- Canvas Elements: For particle system and matrix rain animations
- Data Attributes:
data-targetfor animated counters,data-skillfor circuit connections - Terminal Div: Fixed position interactive terminal simulation
- Form Integration: FormSubmit.co for contact form (no backend required)
Step 3: Implementing Core CSS Styling
Create Design System in css/style.css
/* ===========================
CSS Variables & Reset
=========================== */
:root {
--primary-red: #ef4444;
--dark-bg: #0a0a0a;
--dark-card: #1a1a1a;
--text-gray: #e5e5e5;
--text-dark: #a0a0a0;
--border-gray: #333;
/* Fonts */
--font-heading: 'Orbitron', sans-serif;
--font-body: 'Rajdhani', sans-serif;
--font-mono: 'Courier New', monospace;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-body);
background: var(--dark-bg);
color: var(--text-gray);
line-height: 1.6;
overflow-x: hidden;
}
/* ===========================
Navigation Bar
=========================== */
.navbar {
position: fixed;
top: 0;
width: 100%;
background: rgba(10, 10, 10, 0.95);
backdrop-filter: blur(10px);
z-index: 1000;
border-bottom: 2px solid var(--primary-red);
}
.navbar .container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
max-width: 1400px;
margin: 0 auto;
}
.logo {
font-family: var(--font-heading);
font-size: 2rem;
font-weight: 900;
color: var(--primary-red);
text-decoration: none;
text-shadow: 0 0 10px var(--primary-red);
}
.nav-links {
display: flex;
list-style: none;
gap: 2rem;
}
.nav-links a {
color: var(--text-gray);
text-decoration: none;
font-size: 1.1rem;
font-weight: 600;
transition: color 0.3s ease;
position: relative;
}
.nav-links a:hover {
color: var(--primary-red);
}
.nav-links a::after {
content: '';
position: absolute;
bottom: -5px;
left: 0;
width: 0;
height: 2px;
background: var(--primary-red);
transition: width 0.3s ease;
}
.nav-links a:hover::after {
width: 100%;
}
/* ===========================
Hero Section
=========================== */
.hero {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.hero-content {
text-align: center;
z-index: 10;
position: relative;
}
.hero h1 {
font-family: var(--font-heading);
font-size: 5rem;
font-weight: 900;
color: var(--primary-red);
margin-bottom: 1rem;
text-shadow: 0 0 20px var(--primary-red);
}
.typing-text {
font-size: 1.8rem;
color: var(--text-gray);
min-height: 2.5rem;
font-weight: 500;
}
.typing-text::after {
content: '|';
animation: blink 0.7s infinite;
color: var(--primary-red);
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* ===========================
Buttons
=========================== */
.btn {
display: inline-block;
padding: 1rem 2.5rem;
font-size: 1.1rem;
font-weight: 600;
text-decoration: none;
border-radius: 5px;
transition: all 0.3s ease;
cursor: pointer;
border: none;
font-family: var(--font-body);
}
.btn-primary {
background: var(--primary-red);
color: white;
box-shadow: 0 0 20px rgba(239, 68, 68, 0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 0 30px rgba(239, 68, 68, 0.6);
}
.btn-secondary {
background: transparent;
color: var(--primary-red);
border: 2px solid var(--primary-red);
}
.btn-secondary:hover {
background: var(--primary-red);
color: white;
}
/* ===========================
Glitch Effect
=========================== */
.glitch-text {
position: relative;
display: inline-block;
}
.glitch-text:hover::before,
.glitch-text:hover::after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.glitch-text:hover::before {
animation: glitch-1 0.3s infinite;
color: #ff0000;
z-index: -1;
}
.glitch-text:hover::after {
animation: glitch-2 0.3s infinite;
color: #00ffff;
z-index: -2;
}
@keyframes glitch-1 {
0%, 100% { transform: translate(0); }
33% { transform: translate(-2px, 2px); }
66% { transform: translate(2px, -2px); }
}
@keyframes glitch-2 {
0%, 100% { transform: translate(0); }
33% { transform: translate(2px, -2px); }
66% { transform: translate(-2px, 2px); }
}
/* ===========================
Sections
=========================== */
.section {
padding: 5rem 2rem;
position: relative;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.section-title {
font-family: var(--font-heading);
font-size: 3rem;
text-align: center;
margin-bottom: 3rem;
color: var(--primary-red);
text-shadow: 0 0 15px var(--primary-red);
}
/* ===========================
Stats Counter
=========================== */
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
margin-top: 2rem;
}
.stat-item {
text-align: center;
padding: 2rem;
background: var(--dark-card);
border-radius: 10px;
border: 1px solid var(--border-gray);
}
.stat-number {
font-family: var(--font-mono);
font-size: 3rem;
font-weight: 700;
color: var(--primary-red);
text-shadow: 0 0 15px var(--primary-red);
display: block;
}
.stat-number::after {
content: '+';
}
.stat-label {
font-size: 1.1rem;
color: var(--text-dark);
margin-top: 0.5rem;
display: block;
}
/* ===========================
Responsive Design
=========================== */
@media (max-width: 768px) {
.hero h1 {
font-size: 3rem;
}
.typing-text {
font-size: 1.2rem;
}
.nav-links {
display: none; /* Implement mobile menu */
}
.stats {
grid-template-columns: 1fr;
}
}
CSS Organization Tips
- Variables: Keep all colors, fonts, and spacing in CSS custom properties
- BEM Naming: Use Block-Element-Modifier for complex components
- Mobile-First: Start with mobile styles, use
min-widthmedia queries - Performance: Use
transformandopacityfor animations (GPU-accelerated)
Step 4: Building Advanced Animation Systems
Animation 1: Particle System with Microchip Icons
Create js/animations.js:
/* ===========================
Particle System
=========================== */
class ParticleSystem {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
if (!this.canvas) return;
this.ctx = this.canvas.getContext('2d');
this.particles = [];
this.particleCount = 60;
this.resize();
this.init();
this.animate();
window.addEventListener('resize', () => this.resize());
}
resize() {
this.canvas.width = this.canvas.offsetWidth;
this.canvas.height = this.canvas.offsetHeight;
}
init() {
for (let i = 0; i < this.particleCount; i++) {
this.particles.push({
x: Math.random() * this.canvas.width,
y: Math.random() * this.canvas.height,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
radius: Math.random() * 3 + 1
});
}
}
animate() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Update and draw particles
this.particles.forEach(particle => {
particle.x += particle.vx;
particle.y += particle.vy;
// Wrap around edges
if (particle.x < 0) particle.x = this.canvas.width;
if (particle.x > this.canvas.width) particle.x = 0;
if (particle.y < 0) particle.y = this.canvas.height;
if (particle.y > this.canvas.height) particle.y = 0;
// Draw particle
this.ctx.beginPath();
this.ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
this.ctx.fillStyle = 'rgba(239, 68, 68, 0.6)';
this.ctx.fill();
});
// Draw connections
this.particles.forEach((p1, i) => {
this.particles.slice(i + 1).forEach(p2 => {
const dx = p1.x - p2.x;
const dy = p1.y - p2.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
this.ctx.beginPath();
this.ctx.moveTo(p1.x, p1.y);
this.ctx.lineTo(p2.x, p2.y);
this.ctx.strokeStyle = `rgba(239, 68, 68, ${0.2 * (1 - distance / 150)})`;
this.ctx.lineWidth = 1;
this.ctx.stroke();
}
});
});
requestAnimationFrame(() => this.animate());
}
}
/* ===========================
Matrix Rain Effect
=========================== */
class MatrixRain {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
if (!this.canvas) return;
this.ctx = this.canvas.getContext('2d');
this.chars = '0123456789ABCDEF';
this.fontSize = 14;
this.columns = 0;
this.drops = [];
this.resize();
this.init();
this.animate();
window.addEventListener('resize', () => this.resize());
}
resize() {
this.canvas.width = this.canvas.offsetWidth;
this.canvas.height = this.canvas.offsetHeight;
this.columns = Math.floor(this.canvas.width / this.fontSize);
this.drops = Array(this.columns).fill(1);
}
init() {
this.drops = Array(this.columns).fill(1).map(() => Math.random() * -100);
}
animate() {
// Fade effect
this.ctx.fillStyle = 'rgba(10, 10, 10, 0.05)';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fillStyle = '#ef4444';
this.ctx.font = `${this.fontSize}px monospace`;
for (let i = 0; i < this.drops.length; i++) {
const char = this.chars[Math.floor(Math.random() * this.chars.length)];
const x = i * this.fontSize;
const y = this.drops[i] * this.fontSize;
this.ctx.fillText(char, x, y);
if (y > this.canvas.height && Math.random() > 0.975) {
this.drops[i] = 0;
}
this.drops[i]++;
}
setTimeout(() => requestAnimationFrame(() => this.animate()), 50);
}
}
/* ===========================
Typing Effect
=========================== */
class TypingEffect {
constructor(element, texts, typingSpeed = 100, deletingSpeed = 50, pauseTime = 2000) {
this.element = element;
this.texts = texts;
this.typingSpeed = typingSpeed;
this.deletingSpeed = deletingSpeed;
this.pauseTime = pauseTime;
this.textIndex = 0;
this.charIndex = 0;
this.isDeleting = false;
this.type();
}
type() {
const currentText = this.texts[this.textIndex];
if (this.isDeleting) {
this.element.textContent = currentText.substring(0, this.charIndex - 1);
this.charIndex--;
} else {
this.element.textContent = currentText.substring(0, this.charIndex + 1);
this.charIndex++;
}
let speed = this.isDeleting ? this.deletingSpeed : this.typingSpeed;
if (!this.isDeleting && this.charIndex === currentText.length) {
speed = this.pauseTime;
this.isDeleting = true;
} else if (this.isDeleting && this.charIndex === 0) {
this.isDeleting = false;
this.textIndex = (this.textIndex + 1) % this.texts.length;
speed = 500;
}
setTimeout(() => this.type(), speed);
}
}
/* ===========================
Initialize Animations
=========================== */
document.addEventListener('DOMContentLoaded', () => {
// Particle system
new ParticleSystem('particle-canvas');
// Matrix rain
new MatrixRain('matrix-canvas');
// Typing effect
const typingElement = document.querySelector('.typing-text');
if (typingElement) {
new TypingEffect(typingElement, [
'Embedded Systems Engineer',
'IoT Solutions Architect',
'Firmware Developer',
'Hardware Designer',
'RTOS Specialist'
]);
}
});
Animation System Breakdown
-
Particle System:
- 60 floating particles with random velocity
- Draws connection lines between nearby particles (<150px)
- Uses
requestAnimationFramefor smooth 60fps animation - Auto-resizes canvas on window resize
-
Matrix Rain:
- Falling hex characters (0-9, A-F)
- Fade trail effect using semi-transparent overlay
- Red glow matching theme color
- 50ms delay between frames for authentic “rain” effect
-
Typing Effect:
- Cycles through 5 professional titles
- Types character-by-character at 100ms intervals
- Pauses 2s when complete, then deletes at 50ms intervals
- Smooth transitions with blinking cursor (CSS)
Step 5: Interactive Circuit Node Connections
Create js/circuit-nodes.js:
/* ===========================
Circuit Node Connections for Skills
=========================== */
class CircuitNodes {
constructor() {
this.canvas = document.getElementById('circuit-canvas');
if (!this.canvas) return;
this.ctx = this.canvas.getContext('2d');
this.skillTags = document.querySelectorAll('.skill-tag');
this.activeSkill = null;
// Define skill relationships
this.relationships = {
'C/C++': ['MQTT', 'Protobuf', 'Docker', 'FreeRTOS'],
'ARM Cortex': ['ESP32', 'FreeRTOS', 'I2C/SPI'],
'ESP32': ['MQTT', 'Wi-Fi', 'BLE', 'AWS IoT'],
'Python': ['Docker', 'Linux', 'AWS IoT', 'CI/CD'],
'MQTT': ['AWS IoT', 'Protobuf', 'Docker'],
'FreeRTOS': ['C/C++', 'ARM Cortex', 'I2C/SPI'],
'Docker': ['CI/CD', 'Linux', 'AWS'],
'AWS IoT': ['MQTT', 'Python', 'AWS']
};
this.resize();
this.attachEvents();
window.addEventListener('resize', () => this.resize());
}
resize() {
const section = this.canvas.closest('.section');
this.canvas.width = section.offsetWidth;
this.canvas.height = section.offsetHeight;
}
attachEvents() {
this.skillTags.forEach(tag => {
tag.addEventListener('mouseenter', (e) => this.handleHover(e.target));
tag.addEventListener('mouseleave', () => this.clearConnections());
});
}
handleHover(skill) {
this.activeSkill = skill.getAttribute('data-skill');
const relatedSkills = this.relationships[this.activeSkill] || [];
// Clear canvas
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Highlight active skill
skill.style.background = 'var(--primary-red)';
skill.style.color = 'white';
// Find and highlight related skills
this.skillTags.forEach(tag => {
const skillName = tag.getAttribute('data-skill');
if (relatedSkills.includes(skillName)) {
tag.style.background = 'rgba(239, 68, 68, 0.3)';
tag.style.borderColor = 'var(--primary-red)';
// Draw connection line
this.drawConnection(skill, tag);
}
});
}
drawConnection(from, to) {
const fromRect = from.getBoundingClientRect();
const toRect = to.getBoundingClientRect();
const canvasRect = this.canvas.getBoundingClientRect();
const startX = fromRect.left + fromRect.width / 2 - canvasRect.left;
const startY = fromRect.top + fromRect.height / 2 - canvasRect.top;
const endX = toRect.left + toRect.width / 2 - canvasRect.left;
const endY = toRect.top + toRect.height / 2 - canvasRect.top;
// Draw curved line
this.ctx.beginPath();
this.ctx.moveTo(startX, startY);
const cp1x = startX + (endX - startX) / 3;
const cp1y = startY;
const cp2x = startX + 2 * (endX - startX) / 3;
const cp2y = endY;
this.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY);
this.ctx.strokeStyle = 'rgba(239, 68, 68, 0.6)';
this.ctx.lineWidth = 2;
this.ctx.stroke();
// Draw nodes
this.ctx.beginPath();
this.ctx.arc(startX, startY, 5, 0, Math.PI * 2);
this.ctx.fillStyle = 'var(--primary-red)';
this.ctx.fill();
this.ctx.beginPath();
this.ctx.arc(endX, endY, 5, 0, Math.PI * 2);
this.ctx.fillStyle = 'var(--primary-red)';
this.ctx.fill();
}
clearConnections() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.skillTags.forEach(tag => {
tag.style.background = '';
tag.style.color = '';
tag.style.borderColor = '';
});
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
new CircuitNodes();
});
How Circuit Nodes Work
- Relationship Mapping: Defines which skills are related (e.g., C++ connects to MQTT, Docker, FreeRTOS)
- Hover Detection: Listens for mouseenter on any
.skill-tagelement - Visual Feedback:
- Active skill turns solid red
- Related skills get red tint and border
- Connection Drawing:
- Calculates absolute positions of skill tags
- Draws curved Bézier paths between related skills
- Adds pulsing node indicators at connection points
- Auto-Cleanup: Clears all highlights and lines on mouse leave
Step 6: Embedded Terminal Simulation
Create the terminal command system in js/main.js:
/* ===========================
Terminal Simulation
=========================== */
class EmbeddedTerminal {
constructor() {
this.terminal = document.getElementById('terminal');
this.output = document.getElementById('terminal-output');
this.input = document.getElementById('terminal-input');
if (!this.terminal) return;
this.commands = {
help: this.showHelp.bind(this),
status: this.showStatus.bind(this),
skills: this.showSkills.bind(this),
projects: this.showProjects.bind(this),
contact: this.showContact.bind(this),
about: this.showAbout.bind(this),
clear: this.clearTerminal.bind(this)
};
this.init();
}
init() {
// Boot sequence on load
this.bootSequence();
// Handle input
this.input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
const command = this.input.value.trim().toLowerCase();
this.executeCommand(command);
this.input.value = '';
}
});
// Terminal controls
document.querySelector('.terminal-btn.minimize').addEventListener('click', () => {
this.terminal.classList.toggle('minimized');
});
document.querySelector('.terminal-btn.close').addEventListener('click', () => {
this.terminal.style.display = 'none';
});
}
bootSequence() {
const bootMessages = [
'Initializing system...',
'Loading kernel modules... [OK]',
'Starting network services... [OK]',
'Mounting file systems... [OK]',
'Portfolio system ready.',
'',
'Type "help" for available commands.'
];
bootMessages.forEach((msg, index) => {
setTimeout(() => this.print(msg), index * 500);
});
}
executeCommand(command) {
this.print(`$ ${command}`, 'command');
if (this.commands[command]) {
this.commands[command]();
} else if (command) {
this.print(`Command not found: ${command}. Type "help" for available commands.`, 'error');
}
}
showHelp() {
this.print('Available commands:', 'info');
this.print(' help - Show this help message');
this.print(' status - Show system status');
this.print(' skills - List technical skills');
this.print(' projects - Show featured projects');
this.print(' contact - Display contact information');
this.print(' about - About Deepak Singhal');
this.print(' clear - Clear terminal screen');
}
showStatus() {
this.print('System Status:', 'info');
this.print(' CPU: ARM Cortex-M7 @ 480MHz');
this.print(' Memory: 1MB SRAM');
this.print(' Storage: 2MB Flash');
this.print(' Status: OPERATIONAL');
this.print(' Uptime: 7 years');
}
showSkills() {
this.print('Core Technical Skills:', 'info');
this.print(' • Embedded Systems: C/C++, ARM, ESP32');
this.print(' • IoT: MQTT, AWS IoT, Edge Computing');
this.print(' • RTOS: FreeRTOS, Zephyr');
this.print(' • Protocols: I2C, SPI, UART, CAN');
this.print(' • DevOps: Docker, CI/CD, Linux');
}
showProjects() {
this.print('Featured Projects:', 'info');
this.print(' 1. IoT Sensor Network - Distributed MQTT system');
this.print(' 2. RTOS Task Scheduler - Custom real-time kernel');
this.print(' 3. Industrial Controller - Modbus/PLC integration');
}
showContact() {
this.print('Contact Information:', 'info');
this.print(' Email: deepaksinghal1995@gmail.com');
this.print(' LinkedIn: linkedin.com/in/deepak-singhal');
this.print(' GitHub: github.com/deepaksinghal');
}
showAbout() {
this.print('About Deepak Singhal:', 'info');
this.print(' Embedded Systems Engineer with 7+ years experience');
this.print(' Specializing in IoT, RTOS, and firmware development');
this.print(' Passionate about building scalable embedded solutions');
}
clearTerminal() {
this.output.innerHTML = '';
}
print(message, type = 'normal') {
const line = document.createElement('div');
line.className = `terminal-line terminal-${type}`;
line.textContent = message;
this.output.appendChild(line);
this.output.scrollTop = this.output.scrollHeight;
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
new EmbeddedTerminal();
});
Terminal Features
- Boot Sequence: Auto-runs on page load with realistic firmware messages
- Interactive Commands: 7 working commands (help, status, skills, projects, contact, about, clear)
- Retro UI: Fixed bottom-right position, draggable (optional), minimize/close buttons
- Auto-Scroll: Terminal output scrolls to bottom on new messages
- Error Handling: Shows “command not found” for invalid input
Step 7: AWS EC2 Deployment
Launch EC2 Instance
-
Login to AWS Console → EC2 Dashboard
-
Launch Instance:
- Name: portfolio-website
- AMI: Ubuntu Server 22.04 LTS
- Instance Type: t2.micro (free tier) or t3.small
- Key Pair: Create new or use existing (download .pem file)
- Security Group:
- SSH (22) - Your IP only
- HTTP (80) - 0.0.0.0/0
- HTTPS (443) - 0.0.0.0/0
-
Allocate Elastic IP:
- EC2 → Elastic IPs → Allocate
- Actions → Associate → Select your instance
Configure Domain DNS
Add A record in your domain provider’s DNS settings:
Name: profile
Type: A
Value: <Your Elastic IP>
TTL: 3600
Wait 5-60 minutes for DNS propagation.
Connect to EC2 and Install Nginx
# Connect via SSH
ssh -i "your-key.pem" ubuntu@<your-elastic-ip>
# Update system
sudo apt update && sudo apt upgrade -y
# Install Nginx
sudo apt install nginx -y
# Start and enable Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
# Verify Nginx is running
sudo systemctl status nginx
Upload Website Files
From your local machine:
# Navigate to your project folder
cd Personal/
# Upload files to EC2
scp -i "your-key.pem" -r * ubuntu@<your-elastic-ip>:/tmp/website/
On EC2 instance:
# Move files to web root
sudo rm -rf /var/www/html/*
sudo mv /tmp/website/* /var/www/html/
# Set permissions
sudo chown -R www-data:www-data /var/www/html/
sudo chmod -R 755 /var/www/html/
Configure Nginx Virtual Host
# Create Nginx config
sudo nano /etc/nginx/sites-available/profile.sarcasticrobo.com
Add configuration:
server {
listen 80;
server_name profile.sarcasticrobo.com;
root /var/www/html;
index index.html;
# Main location
location / {
try_files $uri $uri/ =404;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|pdf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}
Enable site:
# Create symlink
sudo ln -s /etc/nginx/sites-available/profile.sarcasticrobo.com /etc/nginx/sites-enabled/
# Remove default
sudo rm /etc/nginx/sites-enabled/default
# Test config
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
Install SSL Certificate (Let’s Encrypt)
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Get certificate and auto-configure Nginx
sudo certbot --nginx -d profile.sarcasticrobo.com
# Follow prompts:
# - Enter email
# - Agree to terms
# - Choose to redirect HTTP to HTTPS (option 2)
# Test auto-renewal
sudo certbot renew --dry-run
Configure Firewall
# Enable UFW
sudo ufw enable
# Allow SSH (important - do this first!)
sudo ufw allow ssh
# Allow Nginx Full (HTTP + HTTPS)
sudo ufw allow 'Nginx Full'
# Check status
sudo ufw status
Set Up Automatic Backups
# Create backup script
sudo nano /usr/local/bin/backup-website.sh
Add script:
#!/bin/bash
BACKUP_DIR="/home/ubuntu/backups"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup directory if not exists
mkdir -p $BACKUP_DIR
# Backup website files
tar -czf $BACKUP_DIR/website_backup_$DATE.tar.gz /var/www/html
# Delete backups older than 7 days
find $BACKUP_DIR -name "website_backup_*.tar.gz" -mtime +7 -delete
echo "Backup completed: website_backup_$DATE.tar.gz"
Make executable and schedule:
# Make script executable
sudo chmod +x /usr/local/bin/backup-website.sh
# Add to crontab (runs daily at 2 AM)
sudo crontab -e
# Add this line:
0 2 * * * /usr/local/bin/backup-website.sh
Test Deployment
- Visit
http://profile.sarcasticrobo.com(should redirect to HTTPS) - Test all animations load correctly
- Submit contact form (remember FormSubmit activation!)
- Check SSL certificate (green padlock in browser)
- Test on mobile devices
Step 8: Contact Form Setup
FormSubmit.co Configuration
The contact form uses FormSubmit.co - no backend required!
Critical First-Time Setup:
- Submit a test message through your live contact form
- Check email at
deepaksinghal1995@gmail.com - Click the activation link in the FormSubmit email
- Submit another test - should now receive emails!
Form Configuration in HTML:
<form id="contact-form" action="https://formsubmit.co/deepaksinghal1995@gmail.com" method="POST">
<!-- Hidden fields -->
<input type="hidden" name="_subject" value="New message from Portfolio Website">
<input type="hidden" name="_captcha" value="false">
<input type="hidden" name="_template" value="table">
<!-- Form fields -->
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<input type="text" name="subject" placeholder="Subject" required>
<textarea name="message" placeholder="Your Message" rows="5" required></textarea>
<button type="submit" class="btn btn-primary">Send Message</button>
</form>
What You’ll Receive:
Each submission sends you an email with:
- Name of sender
- Email address (to reply)
- Subject line
- Message content
- Timestamp
Troubleshooting
Common Issues and Solutions
Issue 1: Animations Not Working
- Check Console: Open DevTools (F12) → Console tab for JavaScript errors
- Verify Canvas IDs: Ensure
<canvas id="particle-canvas">matchesgetElementById('particle-canvas') - Test Browser: Try in different browser (Chrome, Firefox)
- Check File Paths: Verify all JS files load correctly in Network tab
Issue 2: Contact Form Not Sending
- Activation Required: FormSubmit needs one-time activation (check spam folder)
- CORS Errors: FormSubmit works from live domains, not
file://protocol - Email Typo: Verify email address in form action URL
- Form Method: Must be
method="POST"
Issue 3: Website Not Loading on Custom Domain
- DNS Propagation: Wait up to 24 hours (usually 5-60 minutes)
- Check DNS: Use
dig profile.sarcasticrobo.comor online DNS checker - Nginx Config: Run
sudo nginx -tto test configuration - Firewall: Verify ports 80 and 443 are open (
sudo ufw status)
Issue 4: SSL Certificate Errors
- Renew Certificate:
sudo certbot renew - Check Cert Status:
sudo certbot certificates - Nginx Reload:
sudo systemctl reload nginx - Port 443: Ensure HTTPS port is allowed in security group
Issue 5: Slow Animations on Mobile
- Reduce Particle Count: Change
particleCount = 30for mobile - Disable Heavy Effects: Comment out matrix rain on small screens
- Add Media Query: Detect mobile and disable certain animations
if (window.innerWidth < 768) {
// Skip heavy animations on mobile
return;
}
Issue 6: Terminal Not Showing
- Z-Index: Ensure terminal has high z-index (999+)
- Position Fixed: Terminal needs
position: fixedin CSS - Check Display: Verify
display: block(notnone) - JavaScript Errors: Check console for errors in terminal initialization
Going Further
Enhancement Ideas
-
Add Mobile Menu:
- Hamburger icon for navigation
- Slide-in menu panel
- Touch-friendly links
-
Implement Blog Section:
- Technical articles
- Project deep-dives
- Tutorial content
-
Add Analytics:
- Google Analytics for visitor tracking
- Hotjar for user behavior
- Monitor popular sections
-
Performance Optimization:
- Lazy load images
- Minify CSS/JS
- Use WebP images
- Implement service worker for offline support
-
Advanced Animations:
- WebGL for 3D effects
- GSAP for timeline animations
- Intersection Observer for scroll-triggered effects
-
Backend Integration:
- Node.js API for contact form
- Database for blog posts
- Admin panel for content management
-
SEO Improvements:
- Meta tags optimization
- OpenGraph tags for social sharing
- Sitemap.xml
- robots.txt
-
Accessibility Enhancements:
- ARIA labels
- Keyboard navigation
- Screen reader testing
- High contrast mode
Conclusion
You’ve successfully built a professional embedded systems portfolio website featuring:
✅ 12 Advanced Animation Systems - PCB traces, particles, matrix rain, typing, terminal, waveforms, LEDs, circuit nodes, glitch effects, stat counters
✅ Fully Responsive Design - Works seamlessly on desktop, tablet, and mobile
✅ Production Deployment - Live on AWS EC2 with Nginx and SSL
✅ Interactive Features - Terminal simulation, contact form, skill connections
✅ Professional Aesthetic - Dark theme with red accents, embedded systems vibe
Key Learnings
- Canvas API Mastery: Particle systems and matrix rain require understanding of canvas rendering,
requestAnimationFrame, and performance optimization - CSS Animations: Hardware-accelerated transforms (
translate,scale,rotate) provide smooth 60fps animations - Event-Driven Architecture: All interactive features use event listeners for clean, maintainable code
- Deployment Best Practices: Nginx configuration, SSL certificates, security headers, and automated backups are essential for production websites
- User Experience: Animations enhance the experience but must be performance-conscious and mobile-friendly
Next Steps
- Customize Content: Replace all placeholder text with your actual experience and projects
- Add Images: Upload professional profile photo and project screenshots
- Test Thoroughly: Check all features on different devices and browsers
- Monitor Performance: Use Lighthouse to check performance, accessibility, and SEO scores
- Iterate and Improve: Gather feedback and continuously enhance the website
Complete Code Repository
All code for this tutorial is available on GitHub:
Repository: https://github.com/deepak4395/Personal
Live Demo: https://profile.sarcasticrobo.com
File Structure Summary
Personal/
├── index.html (270 lines) # Main HTML structure
├── css/
│ └── style.css (1200+ lines) # Complete styling
├── js/
│ ├── animations.js (300+ lines) # 6 animation systems
│ ├── circuit-nodes.js (150 lines) # Skill connections
│ ├── effects.js (200 lines) # Counters, glitch
│ └── main.js (400 lines) # Terminal, nav, form
└── assets/
└── images/ # Profile and projects
Total Project Stats:
- ~2,500 lines of code
- 12 unique animation features
- 7 interactive terminal commands
- Fully responsive (mobile-first)
- Production-ready with SSL
Congratulations! You now have a professional, animation-rich portfolio website deployed to the web. Happy coding! 🚀
Built with ❤️ by Deepak Singhal
Tutorial Created: January 13, 2026