I Coded My Portfolio From Scratch. No Templates.
Why using pre-built templates stunts your growth as a developer, and how I engineered my own custom 3D web experience from raw code.

There is a very specific kind of regret that hits when you deploy someone else's template, change the name and the profile picture, push it to GitHub, and then look at the result and think — yeah. That's me. That's my work.
It's not. It never was.
I made that mistake once. Grabbed something off a free portfolio site, tweaked the colors a bit, added my name in the hero section. It looked clean. It looked professional even. But whenever someone asked me about it I'd skip over the details because deep down I knew I hadn't built any of it. I had dressed up someone else's house and called it home.
So when I finally decided to sit down and build ahmershah.dev properly, the first rule I set for myself was simple — no template. Not even as a reference. Start from index.html and figure the rest out.
What followed was months of fighting with CSS, arguing with Three.js, debugging scroll behavior at 1 AM, and slowly building something that I can actually explain line by line. This is that story.
Why Templates Are a Trap (For Beginners Especially)
Before I get into the build, I want to actually say something here because I see this conversation come up constantly in developer communities. People ask — should I use a template for my portfolio? And the nice answer is: it depends.
The honest answer is: if you're learning, a template will stunt you.
Using a template means you skip the decisions. You skip figuring out why a section is structured the way it is, why a certain animation works the way it does, why the developer chose to reach for a library instead of writing something custom. You inherit a finished product and you don't get any of the understanding that came from building it.
Now if you're a senior developer trying to ship a portfolio in a weekend because you're job hunting — sure. Template makes sense. Time is money.
But if you're a student, if you're early in your career, if you're trying to prove to yourself and to the people hiring you that you can build things — then building your own portfolio is genuinely one of the best projects you can do. Not because the portfolio itself is impressive. Because the process of building it forces you to answer real questions.
The Stack and Why It Looks the Way It Does
My stack for this project is: HTML5, custom CSS (this is the majority of the codebase, not Bootstrap), Bootstrap 5 for the grid layer only, vanilla JavaScript, a little bit of jQuery (honestly way less than I planned), Three.js, TurnJS, TiltJS, Lenis.js, and AJAX for some of the dynamic loading. Contact form runs through Formspree.
If you look at the repo's language breakdown — it's 50.7% JavaScript, 29.3% CSS, 20% HTML. That CSS number felt right when I saw it. A lot of the complexity in this site lives in the stylesheets.
I want to explain why the stack ended up looking like this because I made some deliberate choices and some accidental ones.
Bootstrap vs Custom CSS — this one matters
The instinct a lot of beginners have is to let Bootstrap do everything. You end up with like 50 utility classes on every div and very little actual understanding of what the layout is doing. I made the decision early on to use Bootstrap only for the grid system and for a few responsive breakpoints. Everything else — spacing, typography, animations, color, positioning — is custom CSS using variables.
This was slower. It was also much better for me as a developer. When something broke I had to understand why it broke instead of just switching utility classes until it looked right.
The result is a CSS file that is actually kind of long and in some places probably not perfectly organized. That's real. I'm not going to pretend the codebase is immaculate. There are sections where I know I could refactor things, places where I wrote a specific rule to fix a very specific layout problem and then never cleaned it up properly. The site works and it looks good but the code behind it has the marks of something that was built iteratively over time, not planned from the start.
JavaScript — mostly vanilla, jQuery at the edges
I planned to use jQuery more. Ended up using it way less. Vanilla JS handles most of the interaction logic. jQuery ended up in a few specific places where I was doing quick DOM manipulation and the syntax was just cleaner.
I don't think there's a strong philosophical argument here. It's just that once you start writing vanilla JS properly you realize most of the things you were reaching for jQuery to do are actually not that hard.
Three.js, a Blender File, and the Learning Curve I Underestimated
The Three.js section of this build took way longer than I thought it would.
Three.js is one of those libraries where the documentation is technically good and also kind of confusing if you haven't done any 3D work before. The concepts — scene, camera, renderer, geometry, material, mesh, light — they make complete sense once you get it. Before you get it, it feels like you're assembling furniture from instructions written in a different language.
I used Blender to create the 3D model that lives in the portfolio. If you've never opened Blender, it is a lot. The interface alone takes a few days to get comfortable with. But for what I needed — modeling a specific object, exporting it in a format Three.js can read — it was the right tool.
The thing nobody tells you about Three.js performance is that 3D on the web is heavy by default and you have to actively fight against that. Geometry complexity, texture resolution, how you're handling the render loop, whether you're correctly disposing of objects when they're not needed — all of this hits your frame rate. I ended up adding a toggle that lets users turn off the 3D effects entirely, specifically for older devices. Some people will land on the site on a five year old phone. The site should still work for them.
Before adding the 3D scene: The hero section was static. Clean, but kind of flat. No real visual depth.
After: There's actual spatial weight to the page. The 3D element gives the viewport a sense of depth that you can't really replicate with CSS tricks alone.
The trade-off is performance. I'll be honest — Three.js adds real weight to the bundle and if you don't handle it carefully your Lighthouse score will tell you. I spent time optimizing how and when the scene initializes, deferring it until after critical content loads.

The Certifications Book That Took a Weird Amount of Work
One of the sections I'm most satisfied with is the certifications section. Instead of a standard grid of certificate images, I built a flipbook — an actual book you can page through — using TurnJS combined with TiltJS for the 3D tilt effect when you hover.
TurnJS is a library that simulates page turning. It uses CSS3 transforms and JavaScript to create a page-flip animation that looks like a real book. Wiring it up to work with the actual certificate content, making it responsive, handling the edge cases around what happens when someone is on a touch device — this took more time than I expected.
TiltJS sits on top of that and adds the parallax tilt effect when you move your mouse over the book. The way the two libraries interact required some careful event handling. There were bugs. There were moments where the tilt was triggering when it shouldn't, or where the page flip animation was cutting off because something in the transition timing was conflicting.
But when it works — and it works now — it's genuinely one of the most distinctive things on the site. Every other developer portfolio has a grid of certificates. Mine has a book.
Scroll Behavior: Lenis, IntersectionObserver, and the Scrolljacking Debate
Smooth scroll is one of those things that sounds like a nice small detail and turns into a rabbit hole.
I'm using Lenis.js for smooth scrolling. Lenis intercepts the native scroll and replaces it with a smoother, physics-based animation. The result is this buttery, almost native-app feel when you scroll through the page. It's subtle but it matters. The difference between scrolling a site with and without Lenis is the difference between dragging something across carpet versus dragging it across glass.
The debate around scrolljacking is real and worth acknowledging. Scrolljacking — controlling or manipulating the scroll behavior beyond the user's direct input — has a bad reputation and for good reason. It can feel jarring, it breaks user expectations, and it's particularly bad for accessibility. I made specific choices here: the Lenis implementation I'm using follows the user's actual scroll direction and speed, it doesn't take over or redirect the scroll, it just smooths the physics. That's different from the aggressive scrolljacking you see on some agency sites where sections snap into place whether you wanted them to or not.
IntersectionObserver is what drives the section animations. Elements animate in when they enter the viewport. I'm not using a library for this — it's native browser API, well supported, and honestly not that complicated once you write it a few times. The performance profile is much better than scroll event listeners, which fire constantly and are easy to abuse.
SignalHub: The Section I've Never Seen on Another Portfolio
The section called SignalHub is the one I get the most questions about.
Most portfolios have a contact section with a few social icons at the bottom. Facebook, GitHub, LinkedIn, maybe Twitter. That's fine. Functional. Forgettable.
SignalHub is a full section built around two sides — a personal side and a professional side. It's my complete social presence, organized by context. The professional side has GitHub, LinkedIn, dev.to. The personal side has the stuff that's more me and less resume. Both sides are clean, navigable, and presented as a real part of the site rather than an afterthought footer row.
The reason I built it this way is that I think there's actually a signal in how a developer organizes their online presence. Showing that I've thought about context — personal vs professional, what belongs where — says something. It's also just more useful for whoever is looking at the site. If a recruiter lands on it they go to the professional side. If another developer is curious about what I'm actually like as a person they go to the other side.
Is it unconventional? Yes. Did some people think it was weird when I described it? Also yes. It's still on the site.
Nine Sections, One Page, Zero Frameworks
The portfolio is a single page application in the most literal sense — it's one HTML file with nine distinct sections: Home, About, Education, Skills, Certification, Blog, Contact, Projects, and SignalHub.
No React. No Vue. No Next.js. Just HTML, CSS, JavaScript, and a set of libraries that handle specific things.
I want to defend this choice because the developer community has a habit of treating framework usage as a marker of seriousness. If you're not using React you're not building real things, that sort of attitude. I think that's backwards.
For a static portfolio site, a framework adds complexity without adding value. React's component model solves problems that a single-page portfolio doesn't have. The virtual DOM solves a reconciliation problem that doesn't exist when your content doesn't change at runtime. You're adding a build step, a node_modules folder, a mental model, and a bundle size — and none of those things make the portfolio better for the people visiting it.
The cursor tracking, the trailing cursor effect, the animations — all of that is JavaScript. Not framework JavaScript. Just JavaScript.
SEO on a Static Site: The Part Most Portfolio Guides Skip
This part took longer than any feature.
I want to go through what's actually implemented because "I set up SEO" is a phrase people say without explaining what that means.
Meta Tags and Open Graph
Every important meta tag is in the head. Open Graph tags so the site previews correctly when shared on social media. Twitter card meta. Canonical URL. Description. Keywords. These are the basics and they take maybe 30 minutes but a lot of portfolio sites don't have them.
JSON-LD Structured Data
This is the thing most developers skip. JSON-LD is a way to embed structured data in your page that search engines can parse to understand what your page is about. For a personal portfolio, the relevant schema types are Person, WebSite, and potentially BreadcrumbList. Having this in place means Google can potentially show rich results — things like your name and links directly in the search results, not just the page title.
llms.txt
If you haven't heard of llms.txt, it's a relatively new convention — a plain text file at the root of your site that describes your site to large language models. The idea is similar to robots.txt but for AI crawlers. I added one because I think it's going to matter more over time as AI-driven search and AI assistants become the primary way people discover content.
robots.txt and sitemap.xml
Both are present. The sitemap is submitted to both Google Search Console and Bing Webmaster Tools. The robots.txt is correctly configured — there's nothing sophisticated to block, so it's mostly permissive, but having it there properly formatted matters.
Cloudinary for Image Delivery
Images are served from Cloudinary in WebP format with PNG fallbacks. WebP images are on average 25-35% smaller than equivalent JPEG or PNG files. When you're serving a portfolio that has project screenshots, certificate images, profile photos — that size difference accumulates. Cloudinary also gives you automatic format selection and responsive image delivery, so mobile users aren't downloading desktop-sized images.
Google and Bing Indexing
The site is verified with both Google Search Console and Bing Webmaster Tools. Manual indexing requests were submitted. I know this because I did it. Not passively waiting for crawlers to find it.
Structured Heading Hierarchy
One H1 per page. H2s for major sections. H3s inside sections where appropriate. This isn't just SEO — it's accessibility. Screen readers navigate by heading structure. If your headings are a mess the page is a mess.
The "Over-Clean" Problem
There is a thing that happens when you have full control over a codebase and you spend too much time on it. Everything starts looking a little too perfect. Every spacing value is consistent, every transition matches, every color is deliberate. And then you look at it from a distance and something feels slightly off. Too polished. Like a room that nobody actually lives in.
I've had a few people describe my portfolio as "over-clean." I understand what they mean. There's something about a site that looks like it was designed in Figma and exported perfectly that can feel slightly removed. Like you're looking at a product shot rather than a person's work.
I don't have a full solution for this. I've thought about adding more intentional asymmetry, rougher textures, sections that break the grid on purpose. Some of the upcoming features I'm working on — a built-in blog, a personal AI integration, a game — those might add the kind of lived-in quality that the site currently lacks.
For now I'm sitting with it. Sometimes clean is clean and that's fine.

What's Coming Next (The Honest Version)
Here are the features I'm actively working on or thinking seriously about:
Personal AI integration — an AI assistant that knows my work, my writing, my projects, and can answer questions about me. This is actually more interesting technically than it sounds because it requires some form of retrieval-augmented generation rather than just wrapping a model API call.
Built-in blog — not links to external posts. Articles that live inside the portfolio itself, properly rendered, indexed, and consistent with the site's design language.
Case studies for projects — right now the project section shows what I built. The plan is to add case studies that show why I built it, what decisions I made, what I'd change. The how matters less than the thinking behind it.
Performance and accessibility audit — Lighthouse scores are okay but there's work left. WCAG 2.1 AA compliance is the target for accessibility. fetchpriority hints for above-the-fold images. Better font loading strategy.
Replacing the current modal with a custom one — the modal system I'm using right now is borrowed. Writing my own gives me full control over the interaction model and removes an external dependency.
The Thing That Actually Mattered
145 commits to the main branch. 23 stars. 7 forks. JavaScript at 50.7% of the codebase.
None of those numbers are the point.
The point is that when someone asks me how the certification flipbook works, I can explain it. When someone asks why I chose Lenis over native smooth scroll, I have an actual answer. When someone asks about the SEO implementation, I don't have to guess — I built it, I know what's in there.
A template can look better than what I built. Probably several of them do. But a template doesn't teach you anything except how to edit someone else's decisions. And if you're at the beginning of your career, decisions — making them, understanding them, owning them — are what you're supposed to be practicing.
Build your own portfolio. Make it messy if you have to. Make decisions you'll regret later and then learn why you regret them. Put your name on something you actually built.
That's the whole thing.
Stack summary: HTML5, custom CSS3, Bootstrap 5 (grid only), Vanilla JS, jQuery (minimal), Three.js, Blender (3D modeling), TurnJS, TiltJS, Lenis.js, AJAX, Formspree, Cloudinary, GitHub Pages / custom domain
Live: ahmershah.dev
GitHub Repo: ahmershahdev/Portfolio
Find Me Across the Web
- ✍️ Medium: @syedahmershah
- 💬 DEV.to: @syedahmershah
- 🧠 Hashnode: @syedahmershah
- 💻 GitHub: @ahmershahdev
- 🔗 LinkedIn: Syed Ahmer Shah
- 🧭 All links: Beacons
- 🌐 Portfolio: ahmershah.dev





