An LLM-Powered Facelift
2026-07-19 △ Technology
If you’ve stayed apprised of all the latest advancements of LLMs, you’ll know that they are now incredibly capable machines that can build literally anything you can imagine. Well, in code anyway. (But I’m sure that they’ll dominate the physical space too in the near future, but I digress…)
Today, I decided to take the accumulated learnings of harnessing these powerful tools that I used to power my W2 and instead employ them to improve my long lost personal projects, including the site from which you are reading this post: https://daniely.im. This domain was initially a small space that I carved out for myself on the web, meant to be an entry point for the world to view all of my proudest creations. That, of course, never panned out as I imagined. Higher priorities in my career or life always trumped any justification for the hours necessary to research techniques and iterate on projects mostly for my own indulgence. Well, AI-assisted coding (or automatic coding as antirez says) completely changed the calculus. We are now at a point in software engineering where the time-consuming, annoying, or run-of-the-mill boilerplate programming tasks–the not-so-fun parts of programming–can be completely shoved under the rug with a well-crafted prompt. This means that the previous limitations in time, effort, and patience required to build the product you desire are significantly reduced; we’re now only limited by our own ambitions and imagination.
The Ambition
There are so many kinds of personal websites out there. I see many of my SWE peers standing up a domain of their own with varying degrees of effort. Most are just static HTML pages intended to be a public curriculum vitae for interested parties. But given my background in frontend engineering, I felt that there was an onus on me to use my website to showcase the full extent of my work.
With that on my shoulders, I’ve always had strong opinions of how my personal website should look: It should load quickly; it should be slick. It should strive to be simple as possible on the surface, but with enough complexity that enlightens the overall experience. It shouldn’t be too loud or too provocative, nor should it be banal. Most importantly, it should represent me. And what others might assume of my capabilities given my profession.
Fighting against ever decreasing motivation, I decided to take the plunge and publish something simple a few years ago during COVID lockdowns (2021):

It’s a very simple site that follows a cheeky dictionary-like theme: a word, a pronunciation, a definition and related words. Much inspiration was taken from how Oxford structured their entries (though the one pictured appears to be doctored in order to deliver a joke).

While it felt good to have something out there on the web, there were parts of the site that never really functioned exactly how I had imagined it, but alas, time, effort, and patience were too scarce to do anything about it…
That is, Until Now
Fast forward to 2026: LLMs are here ready to do your bidding. They’re efficient, infinitely patient, and are just down to build anything that your heart desires so as long as you stay under your token allowance.
My original ambitions for the website was to not only convey information but to also serve an experience. That meant a tasteful collection of animations, chained transitions, and special effects to give a dull dictionary some polish. As I was doing some research on interactivity, my past experience steered me towards Framer Motion. Much to my surprise, they’ve gone completely pro since I last checked. Their front page is no longer a Github repo but a very polished marketing page touting a full-blown website builder IDE, a la Dreamweaver. Framer seems to have ascended to be in competition with Webflow and is surely beyond the simple animation library that I remembered.
When I asked Claude about the kinds of animations I wanted to build, it thoughtfully arranged the pros and cons of using Framer but also offered to fill me in about GSAP, or the GreenSock Animation Platform. I haven’t heard about this library before, but after reading the timeline API and nodding along in agreement, I knew that I had to give it a try.
What I’ve written up below is the adventure I’ve had in building various aspects of the site using GSAP.
Animation Chaining / Queuing
There are many primitives available to ‘good enough’ craft animations in vanilla JS and CSS, but to get the professional quality animations that you’ve always dreamed of, you’ll need to update your tooling and an expressive DSL to crisply define the intended interaction. This is where GSAP’s timeline API shines.
Specifically for my website, I wanted to chain together the following in sequence:
- Complete the page transition
- Trigger the typing animation of the header word
- Trigger the outro for the cursor once done typing
- Fade in the pronunciation and definition body
- Fade in the “See Also” section
If I wanted to wire this by hand, it either be a rat’s nest of callbacks or a long chain of Promises with some serious time accounting. I don’t want to do any of that. So here comes GSAP:
const tl = gsap.timeline();
tlRef.current = tl;
typeHeaderText(tl, headerTextEl, text, speed);
tl.add(buildCursorOutro(cursorEl), 'typed');
// And a little more hand-waving for the fade-in effects
tl.add(fadeIn(body), 'typed');
tl.add(fadeIn(seeAlso), 'typed');
Very easy. And best of all, Claude is very good navigating its way through this API.
Page Transitions
daniely.im is a Next.js app and acts like a SPA for the most part. Though you can get fancy and build server-side components with Next, I use none of that and simply rely on the router to map URL paths to JS bundles. Often these navigation events are painfully simple. If you’re building a data intensive webapp, you may add a Suspense loading spinner somewhere on your UI, but more often than not, route transitions are dull, synchronized updates to a part of or the entire screen.. I wanted something akin to a page turn where the current route is a layer on top of the next, and the screen peels to reveal the next layer.


How was this achieved? We mount a special intermediary component called a WipeTransition which prepares for the transition by taking a snapshot of the current page before allowing the router to navigate to the next page. With a snapshot of the old page, we are able to render a simple animation of a banded sweep bar where one side is the old and the other is the “new”/current route.
Text Expansion
Another trick: “to be simple on the surface but to hold hidden complexity”. Here I wanted this idea of momentary expanding and collapsing tokens so that by default we see the concatenated view, but if the user seeks more information, they can hover over to gain the context in-situ.
This required a few iterations to get right. The main challenges here are:
- When replacing text, accurately measuring the replaced text’s width and rendering that space in the layout in order fill the space with ‘typed’ characters shortly after
- When expanding text, tokenizing each word, measuring their width, and then injecting those elements inline to ensure that text wrapping will still work
- Defining the behavior and impact for mobile users since they don’t have a cursor hover state


All in all, the interaction feels as if someone were to wrest control over your keyboard and started typing where the cursor was positioned. Those who have worked collaboratively in Google Docs know all too well how this interaction feels. But as a simulation of that behavior, it was moderately difficult to recreate due to the edge cases.
Typing Simulation
This effect was the least technical to execute, but the effort to reward ratio was very high so it’s worth mentioning. Throughout the site, you’ll see headers, underlined words, and numbers animate intermediate values (blank space -> character or many unrelated characters settling to the real value over time).
These too are controlled by GSAP’s Timeline API, for example:
const tl = gsap.timeline({ delay });
for (let i = 1; i <= SCRAMBLE_TICKS; i += 1) {
const isLast = i === SCRAMBLE_TICKS;
const offset = scrambleEase(i / SCRAMBLE_TICKS) * SCRAMBLE_DURATION;
tl.call(
() => setDisplay(isLast ? finalText : randomDigits(finalText.length)),
undefined,
offset,
);
}
Conclusion
In conclusion, this was all a very exciting exercise of being able to achieve some ambitious transitions that would’ve otherwise been a nightmare to embark on my own. Thanks to LLMs, polish projects like these are fun again. In this mode of operation, your contributions towards the end result mostly become: 1) how well you can convey the vision you have in your head into prose and 2) how to make the output of the code AI-legible so that the agent has a feedback loop to work against.
My hope is that this semi-rant-y and prosaic post helps motivate someone to attempt ambitious animations of their own. It’s no longer treacherous or reserved for the professional marketers and designers! At the very least, take a gander at the GSAP Showcase sites and be amazed at what’s possible with the modern web.