Read Time

10 min

Read Time

10 min

Read Time

10 min

Read Time

10 min

Vansh.fyi: About this website

2025

Personal

Gen AI

Vibe Coding

After years of my work living on a Behance profile that felt outdated and creatively restrictive, I knew my personal website couldn't be just another template. I wanted to create a personal brand that showcased my true potential and multifaceted personality, something a portfolio template or a single style could never do justice.

The solution I came up was a dual-mode experience: a "Professional Mode" for the pragmatist and a "Dream Mode" for the artist. This allows visitors to choose the lens through which they see my work, offering both a clean, industry-standard view and a deep dive into my creative soul and my way of thinking in different aspects.

My Role

UI Design

UI Design

UI Design

UI Design

Vibe Coding

Vibe Coding

Vibe Coding

Vibe Coding

Framer Development

Framer Development

Framer Development

Framer Development

Design System

Design System

Design System

Design System

Interaction Design

Interaction Design

Interaction Design

Interaction Design

UI artist

UI artist

UI artist

UI artist

Brand Identity

Brand Identity

Brand Identity

Brand Identity

Usability Testing

Usability Testing

Usability Testing

Usability Testing

Tools Used

Framer

Framer

Framer

Framer

Figma

Figma

Figma

Figma

Whisk

Whisk

Whisk

Whisk

Eleven Labs

Eleven Labs

Eleven Labs

Eleven Labs

Suno

Suno

Suno

Suno

Gemini

Gemini

Gemini

Gemini

ChatGPT

ChatGPT

ChatGPT

ChatGPT

Claude

Claude

Claude

Claude

Genspark

Genspark

Genspark

Genspark

Designing a Duality

Professional Mode is my handshake to the industry featuring a clean, minimalist, and intuitive UX designed for recruiters and stakeholders who need to get straight to the point. It’s built to demonstrate my ability to deliver clear, effective, and user-friendly design solutions.

Dream Mode is where my inner world comes to life. It’s an erratic, artistic, and deeply personal experience where every page is a different work of art, reflecting the many different sides of my personality.

Each page in Dream Mode is a nod to my inspirations in respective domains of my work and life: the homepage is inspired by DreamWorks, their movies defined my mindset greatly.

The Projects page showcases my approach to work, how I treat everything like a physics experiment. The colours are inspired by Blade Runner 2049(a work of art).

The blogs page showcases my love for reading and a future aspiration to publish my research. My intention was to generate a feeling of being inside a library.

The About page showcases my love for storytelling, how I like to talk about my life as well as presenting ideas. This is inspired by manga and Into the Spiderverse my personal favourite genres in showcasing multifaceted concepts like this.

The contact page is inspired by music. As someone who plays a guitar and is obsessed with rhytmns and tuning not only between notes but also between people, I chose this theme. The idea is to showcase my aspiration that my style of working should be in tune and feel like jamming with my co-workers or clients.


The website has subtle metaphors in some places. For instance in the contact page for both modes, the hero's text is in the shape of a funnel on purpose.

Dream Mode is a curated chaos that truly defines my thinking.


Professional Mode's goal on the other hand is to show how the output of that very divergent and chaotic thinking.

a Three month journey

This project was a three-month labor of love, starting with extensive personal ideation and mood boarding in Figma. I treated each page of Dream Mode as its own mini-project, defining a unique visual language before building a cohesive colour and typography system in to govern it all.

The entire site was then built and implemented in Framer. This allowed me to translate my static designs into a dynamic experience with the complex animations, page transitions, and custom code overrides needed to bring the dual-mode concept to life.

Key Features

For Dream Mode, I created a cinematic intro with speech with Master Oogway's voice generated from Any voice Lab and background music from Suno. I edited everything together in Garageband to create an immersive, story-like experience from the very first click.

Oogway's Voice

0:00/1:34

Updated voice

0:00/1:34

I updated the voice from another voice generated from Eleven Labs and changed the background audio to fit her voice. I did so to avoid any copyright claims in future and I realised its unethical to use Randall Duk Kim's voice this way.

import { useEffect } from "react"
import { animate, Override } from "framer"

const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
/*─────────────────────────────────────────────────────────
  Cue list (timestamps in seconds)
─────────────────────────────────────────────────────────*/
const cues = [
    { t: 1.4, id: "line1" },
    { t: 2.76, id: "line2a" },
    { t: 4.46, id: "line2b" },
    { t: 5.96, id: "line2c" },
    { t: 8.15, id: "line3" },
    { t: 8.69, id: "line4a" },
    { t: 10.69, id: "line4b" },
    { t: 14.07, id: "line5" },
    { t: 15.9, id: "fadeOut" },
]

/*─────────────────────────────────────────────────────────
  1 ▸  Overlay frame — keep ID so we can animate it
─────────────────────────────────────────────────────────*/
export const IntroOverlay: Override = () => ({
    id: "IntroOverlay",
})

/*─────────────────────────────────────────
 2 ▸ DREAM-MODE button  –  play-pause-reset
─────────────────────────────────────────*/
export const StartDream: Override = () => {
    console.log("StartDream override attached") // optional debug

    return {
        async onTap() {
            console.log("Dream button tapped") // optional debug

            const overlay = document.getElementById("ModeOverlay")
            const prompt = document.getElementById("volumePrompt")
            const countdown = document.getElementById("countdown")
            const audio = document.getElementById(
                "introAudio"
            ) as HTMLAudioElement | null
            if (!audio) return

            /* 0 ───────────────────────────── Safari unlock */
            audio.muted = true
            audio.currentTime = 0
            await audio.play().catch(console.error) // inside gesture → allowed
            audio.pause() // stop instantly
            audio.currentTime = 0 // rewind
            audio.muted = false // un-mute

            /* 1 ─ hide overlay immediately */
            if (overlay) {
                overlay.style.pointerEvents = "none"
                await animate(
                    "#ModeOverlay",
                    { opacity: 0 },
                    { duration: 0.25 }
                )
                overlay.remove()
            }

            /* 2 ─ show “Turn up volume” prompt */
            if (prompt) prompt.style.opacity = "1"

            for (let i = 5; i >= 1; i--) {
                const textElement = countdown.querySelector("p")
                if (textElement) {
                    textElement.textContent = `Entering the dream in ${i}`
                } else {
                    // Fallback for safety
                    countdown.textContent = `Entering the dream in ${i}`
                }
                await wait(1100)
            }
            /* 3 ─ start narration after 4.7 s */
            setTimeout(() => {
                audio.play().catch(console.error) // now Safari allows
            }, 100)

            /* 4 ─ fade prompt out after 5.0 s total */
            if (prompt) {
                await animate(
                    "#volumePrompt",
                    { opacity: 0 },
                    { delay: 0.4, duration: 0.3 }
                )
                prompt.remove()
            }
        },
    }
}
/*─────────────────────────────────────────────────────────
  3 ▸  PROFESSIONAL MODE button — simple link
─────────────────────────────────────────────────────────*/
/* PROFESSIONAL MODE button — simple navigation */
export const GoProfessional: Override = () => ({
    onTap() {
        window.location.href = "/old-home"
    },
})

/*─────────────────────────────────────────────────────────
  4 ▸  Audio / cue runner
      (fires only after Dream-mode button plays the track)
─────────────────────────────────────────────────────────*/
export const IntroAudio: Override = () => {
    useEffect(() => {
        const audio = document.getElementById(
            "introAudio"
        ) as HTMLAudioElement | null
        if (!audio) return

        /* Run cue loop only when audio is actually playing */
        audio.addEventListener("playing", () => {
            const step = () => {
                const t = audio.currentTime
                cues.forEach(({ id, t: cue }) => {
                    const node = document.getElementById(
                        id
                    ) as HTMLElement | null
                    if (node && !node.dataset.shown && t >= cue) {
                        node.dataset.shown = "true"
                        trigger(id, node)
                    }
                })
                if (!audio.paused) requestAnimationFrame(step)
            }
            requestAnimationFrame(step)
            /* 1-B  Fade in “Skip intro” after 3 seconds  */
            setTimeout(() => {
                const btn = document.getElementById("skipBtn")
                if (btn) animate(btn, { opacity: 1 }, { duration: 1 })
            }, 3000) // 3000 ms = 3 s
        })
    }, [])

    return {}
}

/*─────────────────────────────────────────────────────────
  5 ▸  Skip-intro link  (optional layer)
─────────────────────────────────────────────────────────*/
export const SkipIntro: Override = () => ({
    onTap() {
        const audio = document.getElementById(
            "introAudio"
        ) as HTMLAudioElement | null
        audio?.pause()

        animate(
            "#IntroOverlay",
            { opacity: 0 },
            {
                duration: 0.4,
                onComplete: () => {
                    localStorage.setItem("dreamSeen", "true")
                    window.location.href = "/old-home"
                },
            }
        )
    },
})

/*─────────────────────────────────────────────────────────
  6 ▸  Trigger helper
─────────────────────────────────────────────────────────*/
function trigger(id: string, node?: HTMLElement) {
    if (id === "fadeOut") {
        animate(
            "#IntroOverlay",
            { opacity: 0 },
            {
                duration: 0.8,
                onComplete: () => (window.location.href = "/old-home"),
            }
        )
    } else if (node) {
        node.style.opacity = "0"
        animate(node, { opacity: 1 }, { duration: 0.6, ease: "easeOut" })
    }
}

The code override for implementing this voice to match seamlessly with the words displayed required manual editing to get the timing just right, as AI-generated solutions fell short in syncing and switching modes well.

During usability testing of this intro, I discovered users were either caught off guard by the audio or thought the site was stuck. I iterated on this by adding a 5-second countdown with "Turn Up the Volume" prompt , updating them on their status and giving them time to turn up the volume for dream mode.

The highlight of the About page is the "Spider-Verse" glitch. Using Chain of Thought prompting with Gemini and One shot prompting with Genspark, I generated two Framer code overrides: one to manage the visual glitch effect with sound, and another to seamlessly switch the navigation bar's style as the user scrolls.

I focused on UX enhancements. I designed a sticky navigation panel for the case study pages, allowing users to easily skim through headers without having to scroll through the entire page.

Usability Testing Aftermath

After launching my website live. I spread the links to design groups, developers in my circle(they are amazing at finding edge cases) and hiring managers for job applications to put my concept to test.

I used Microsoft Clarity to view the recordings. And the results were dissapointing. Users were not viewing or viewing Dream Mode for more than 2 seconds.

I decided to understand the reason further by setting up interviews or conversing with them via text with some of them to gather the insights over dream mode. They had one common reason: "It is too oversimulating. We love the references and all but the experience itself is too much."

And the decision was simple. I removed the dream mode from this website. Since then the user retention and engagement with this website has risen significantly.

One more suggestions from users was to change the images in my home and about. They look generic. So I created some stylistic images on Google Whisk and updated them over the website.

Finally I noticed user clicking these icons meant to showcase an overview of my skills, But they had no significant actions for users to make sense, So I added hover states to them.

Takeaways & Reflection

The biggest challenge was maintaining content consistency across two radically different visual systems while tackling the steep technical learning curve of the custom interactions. But in the end what was harder was to let it go. But these decisions are necessary for an increased engagement.


In the end, I created more than a portfolio, a space that proves I can deliver the clean, effective design the industry demands.

You are here