
molenda.dev
Hi, Hubert here đź‘‹
I’m Webflow-native, GSAP enjoyer, UX-capable. Web dev that can do design, improve SEO and speed scores. My core value is to make every pixel look perfect – sometimes with a bit of 3D magic. I’ve made sites for tech clients as well as boutique brands.
See my works and say hi!
gsap
custom interactions
Pre-release
Live site


motion.js
integrations
Live site



gsap
design: Me!
Live site

motion.js
3D: Me!
design: Me!
Work in progress

Favorites
molenda.dev
iCloud
Inbox
Drafts
Sent
Trash
Archived
Inbox
2137 messages, 6 unread
molenda.dev
Inbox - iCloud 8:10
More 3D
We do be cooking but in Blender, alright? Also, why is it called Blender??
molenda.dev
Inbox - iCloud 8:10
3D UI Examples
Yep, I put that xd next to the bladder. No regrets. Sims 3 inspired.
molenda.dev
Inbox - iCloud 8:10
Festival Lineup UI
So this was a dead project but a fun one. Okay, every project is fun but this was...
molenda.dev
Inbox - iCloud 8:10
Photo Dump
Who needs an instagram account when they can do this cursed mail app bootleg...
molenda.dev
Inbox - iCloud 8:10
GSAP Code Example
Who needs a github repo when they can do this cursed mail app bootleg, right?












gsap.registerPlugin(SplitText);
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
function inView(selectors, animationFns, options = {}) {
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !entry.target.dataset.animated) {
entry.target.dataset.animated = 'true';
const fns = Array.isArray(animationFns) ? animationFns : [animationFns];
fns.forEach((fn) => fn(entry.target));
obs.unobserve(entry.target);
}
});
}, { threshold: 0, ...options });
(Array.isArray(selectors) ? selectors : [selectors]).forEach((selector) => {
document.querySelectorAll(selector).forEach((el) => observer.observe(el));
});
}
const Presets = {
fade: (y = 60, duration = 0.3, delay = 0, stagger = 0) => [
{ opacity: 0, autoAlpha: 0, y },
{ opacity: 1, autoAlpha: 1, y: 0, duration, stagger, delay, ease: 'power2.out' },
],
opacity: (duration = 0.3, delay = 0, stagger = 0) => [
{ opacity: 0, autoAlpha: 0 },
{ opacity: 1, autoAlpha: 1, duration, stagger, delay, ease: 'power2.out' },
],
headerAnim: (y = 80, duration = 0.5) => [
{ y, opacity: 0, autoAlpha: 0 },
{ y: 0, opacity: 1, autoAlpha: 1, duration, stagger: 0.02, ease: 'power2.out' },
],
mask: () => [
{ maskSize: '0% 100%' },
{ maskSize: '100% 100%', duration: 0.6, delay: 0.2, ease: 'power1.out' },
],
};
const Handlers = {
logo: (target) => {
gsap.fromTo(
target.querySelectorAll('[data-gsap="fade"]'),
Presets.fade()[0],
Presets.fade(60, .3, 1.3)[1]
);
},
heading: (target) => {
gsap.from(
target.querySelectorAll('.line'), {
rotationX: -10,
rotationZ: -1,
transformOrigin: "50% 50% -160px",
opacity: 0,
duration: 0.5,
ease: "power3",
stagger: 0.3
})
},
button: (target) => {
gsap.fromTo(
target.querySelectorAll('[data-gsap="line"], [data-gsap="buttons"] a'),
Presets.fade(-60)[0],
Presets.fade(0, .3, 1.2, .25)[1]
);
},
elements: (target) => {
gsap.fromTo(
target.querySelectorAll('[data-gsap="line"]'),
Presets.fade()[0],
Presets.fade(0, .3, 0, .15)[1]
);
},
logo2: (target) => {
gsap.fromTo(
target.querySelectorAll('[data-gsap="fade"]'),
Presets.fade()[0],
Presets.fade(60, .3, .3)[1]
);
},
mail: (target) => {
gsap.fromTo(
target.querySelector('[data-gsap="mask"]'),
Presets.mask()[0],
Presets.mask()[1]
)
},
calendar: () => {
gsap.to(".home_launch-calendar_weeks div", { y: -60, duration: 0 })
let t1 = gsap.timeline({ defaults: { duration: .25, ease: "power3" } });
t1.to(".home_launch-calendar_tile", { x: "0rem" })
.to(".home_launch-calendar_tile", { x: "13.5rem" }, "+=.1")
.to(".home_launch-calendar_weeks div", { y: 0, opacity: 1 }, "-=.35")
.to(".home_launch-calendar_tile", { x: "40.5rem" }, "+=.1")
.to(".home_launch-calendar_tile", { x: "54rem" }, "+=.1")
// .to(".home_launch-calendar_tile", { x: "67.5rem" }, "+=.1")
// .to(".home_launch-calendar_tile", { x: "81rem" }, "+=.1")
.to(".home_launch-calendar_glow", { opacity: 1, duration: 1.6 }, "-=.1")
gsap.to(".home_launch-calendar_tile", { opacity: 1, duration: .6 })
},
links: (target) => {
gsap.fromTo(
target.querySelectorAll('a'),
Presets.fade(50)[0],
Presets.fade(0, .3, 0, .06)[1]
);
gsap.fromTo(
target.querySelectorAll('.hide-mobile-landscape'),
Presets.fade(0)[0],
Presets.fade(0, .3, .3, .06)[1]
);
},
radar: (target) => {
const resetTransform = { rotation: 0, skewX: 0, scaleX: 1, scaleY: 1, x: 0, y: 0 }
let t2 = gsap.timeline({ defaults: { duration: .4, ease: "power4.out" } });
t2.to("#line-1", resetTransform)
.to("#line-2", resetTransform, "-=.4")
.to("#rect", { opacity: 1 }, "-=.1")
.to("#circle-1", resetTransform, "-=.1")
.to("#line-3, #line-5", resetTransform, "+=.1")
.to("#line-4, #line-6", resetTransform, "-=.4")
.to("#circle-2", resetTransform, "-=.2")
gsap.fromTo(
target.querySelectorAll('.dot'),
Presets.opacity()[0],
Presets.opacity(.3, .3, .1)[1]
);
},
};
function gsapInit() {
if (mediaQuery.matches) return;
SplitText.create("[data-gsap='lines']", {
type: "lines",
linesClass: "line",
tag: "span",
});
inView('#start', [Handlers.heading, Handlers.logo, Handlers.button]);
inView('#about', Handlers.heading, { threshold: 0.4 });
inView('#waitlist', [Handlers.elements, Handlers.mail], { threshold: 0.4 })
inView('#launch', [Handlers.elements, Handlers.calendar], { threshold: 0.4 })
inView('#footer', [Handlers.elements, Handlers.logo2, Handlers.links, Handlers.radar], { threshold: 0.4 })
}
document.addEventListener('DOMContentLoaded', () => {
let eyeInstance = eye();
gsapInit()
eyeInstance
inView("#timer", timer, { threshold: 0.4 })
radarInit()
});