From our sponsor: Ready to show your plugin skills? Enter the Penpot Plugins Contest (Nov 15-Dec 15) to win cash prizes!
Designing your own portfolio: Angel or Demon?
Having the opportunity to design your portfolio is a process that is both exciting and extremely challenging. Anyone who has gone through this process knows the feeling of seeing endless possibilities. The lack of deadlines can make you feel freer during the process; however, you need a great capacity for decision-making to move forward and not get stuck in a loop.
Discovery and Design Phase
As a young designer, I still have many aspects of myself to discover and refine, such as: what my style is or what I want to convey as a designer. My main goal with this project has always been to enjoy it, and with that, to create my first own product in which I could demonstrate the excitement that designing gives me and my eagerness to innovate.
Feeling that I am in this stage of discovery, I didn’t want to give the website a very strong branding. I preferred to create something with a more neutral look, where the images, text, animations, and transitions would be the protagonists of the experience.
The design process
There are several steps I follow from the moment I start a project until its launch. The first one is always to gather inspiration, which doesn’t have to be exclusively from design; it can be any detail of interest I encounter during the exploration phase. I collect all this inspiration into five different moodboards: UI, typography, motion, photography and miscellaneous.
Then, the creative phase begins. At this stage of design concept phase, I use both my own creativity and a personal list, which I update very often, where I note down different ideas, styles, types of typography, navigation, effects, and much more. I then decide to combine those I deem suitable for the product in mind to achieve various results.
Finally, once the concept is clear and visualized, I proceed to set up a grid structure appropriate for the product and create the screens I have in mind. Alongside this process, I always like to create the more complex transitions and effects in After Effects to make it easier for the developer. Additionally, I document the main details in the Figma file for a better understanding of how everything should work, and also, ask some questions related to creativity or technology I want to ask to Lucas (Nam Hai).
From there, I also encouraged the developer to propose ideas; enjoying the creativity of a creative developer is always enriching.
Once the design phase was completed, it was time to get in touch with developers who had the skills required for the project. At that moment, I was clear that I wanted to collaborate with someone at a similar stage as me, a young person, growing and passionate about what they do. That’s when Lucas (Nam Hai) appeared to me and brought all the ideas I had in mind to life.
Getting ready for the development fase
Our working process, in a nutshell, consisted of defining a comprehension phase to establish a workflow, discovering the possibilities we had in terms of design/technology, and observing the behavior of the various interactions, transitions, effects, etc.
Development Phase: Crafting a Designer Portfolio with Nuxt 3, OGL, and Lenis
Creating a portfolio for a designer requires blend of aesthetic appeal and cutting-edge functionality. I’ll take you through the technical journey of building a highly designed portfolio website.
The Tech Stack
- Frontend Framework: Nuxt3. Serves as the backbone of the project, chosen primarily for its outstanding Developer Experience (DX). The auto-import features and modular architecture allow for streamlined development and efficient builds. While I utilized only a few of Nuxt3’s built-in tools, its capabilities were instrumental in maintaining a smooth workflow.
- WebGL with OGL. A lightweight WebGL library that drives the site’s visual effects.
- Smooth Scrolling with Lenis. It enhances the user interface with smooth, seamless scrolling, crucial for maintaining an uninterrupted flow as users navigate through the designer’s work.
- Custom Animations and Routing. I think we have been all struggling with Nuxt transition api at some point, that’s why I made a custom Routing solution for Vue of my own which allow me to make custom and seamless transition between pages, it is a very unmaintained library that you can find here for inspiration.
All of the magic would not be possible without an animation library. I use my own Raf manager and an handmade animation library too. This gives me full control over my animations makes debuging a lot faster.
Relying on handmade solutions or very simple libraries (besides Nuxt) makes the development process marginally faster and efficient.
Blending the DOM and WebGL
One of the main point of friction when building a webgl experience is to blend the DOM and the WebGL scene. How to make an event in the DOM be received in the webGL scene.
One of the micro-interactions that adds significant flair to the website is the cursor color reacting to the image underneath. This micro interaction is a perfect example of power of Vue’s reactivity to create a seamless blend of the webGL scene and the html template. The background color detection is dealt by the webGL scene with the Picking Technique:
(this.gl as WebGL2RenderingContext).readBuffer((this.gl as WebGL2RenderingContext).COLOR_ATTACHMENT0);
const dataLumos = new Uint8Array(4);
this.gl.readPixels(
mouse.value.x * this.dpr / this.renderTargetRatio,
(vh.value - mouse.value.y) * this.dpr / this.renderTargetRatio,
1,
1,
this.gl.RGBA, // format
this.gl.UNSIGNED_BYTE, // type
dataLumos); // typed array to hold result
const indexLumos = (dataLumos[0] * 0.2126 + dataLumos[1] * 0.7152 + dataLumos[2] * 0.0722) / 255;
lumosThreshold.value = indexLumos >= 0.5;
Here is the code to read the pixel value under the cursor. I use vue reactivity to several degree here : first to store the mouse coordinate (mouse)
, and viewport value (vh
for viewport height). And then with the Ref lumosThreshold
that is updated based on the luminosity of the pixel underneath the cursor. lumosThreshold
is shared between my webGL scene and my components through a store:
<template>
<div :class="{ dark: lumosThreshold }" ref="wrapperRef">
///////////////
// cursor template ...
///////////////
</div>
</template>
<script lang="ts" setup>
const { lumosTreshold } = useCursorStore()
///////////////
// cursor internal logic ...
///////////////
</script>
Conclusions
The project has been very challenging; the ideas were ambitious and required a high level of creativity and technical skill. Both of us are very happy and satisfied with the great result and impact that the website has had on a professional level and within the community. Undoubtedly, we have had many learning experiences from each other in this collaboration, which has allowed us to push our ideas to the maximum. It has been a privilege to work together.
You can follow our work or feel free to ask!
Contact us at:
Oscar Pico — Email · Twitter · LinkedIn
Nam Hai — Email · Twitter · LinkedIn