PX PUSH is a subscription design studio constructed for Freedom Tech, the decentralisation and open financial motion, and one of the crucial underserved sectors in design. Groups constructing right here transfer quick and ship in public, however companies and studios round them hardly ever communicate that language. We needed the web site to replicate how the enterprise itself works: structured, accountable, and barely deadpan about the entire thing.
The PX PUSH id attracts closely from Nineteen Eighties company tradition: bureaucratic language, workplace gear, software program packaging, and the type of organisation that may stamp a doc “authorised for continued use” with out irony. We let that interval govern all the things else: the customer is trying on the website by way of an previous convex CRT monitor, a simulated system sitting completely between them and the content material, on each web page.
The general design ought to reply the identical query: what would this appear like on a display from 1988?
Stack
- Nuxt 3 — SSR/SSG, file-based routing, the entire app.
- GSAP + ScrollTrigger — drives practically each scroll-based reveal, parallax, and hover state on the location, wired by way of a small customized attribute system constructed as soon as and reused per impact.
- Lenis — clean scroll, ticked manually off a single
tickLenishook on the GSAP clock, holding scroll interpolation and eachScrollTriggeron one heartbeat. Disabled outright on cellular in favor of native scroll. - Three.js — three separate builds, every fixing a distinct downside: an extruded chrome emblem, a cloud-flight hero, a floppy disk within the pricing part.
- @nuxt/content material — the Journal runs on markdown information, queried at construct and request time, no exterior CMS.
- Splitting.js — splits headings and paragraphs into strains and phrases within the DOM, feeding the reveal animations.
The Display screen You’re Trying Via
To create the CRT impact, I saved the complete simulation inside a single element, Wrapper.vue, mounted above the remainder of the applying. The impact is constructed from a small stack of fixed-position overlays. A scanline bar sweeps backside to prime on an 8-second loop, a faint repeating line sample that simulates CRT refresh strains, a gentle backdrop-filter: blur layer to create a light-weight bloom impact on previous glass, and a full-screen crt.png overlay, stretched throughout the viewport to darken and deform the perimeters just like the curved glass of an previous monitor. A closing animated noise texture provides a restrained layer of display static.
The entire impact is constructed from CSS and two pictures, working constantly, in all places, for the lifetime of the session:
.scanlines::earlier than {
background: rgba(0, 0, 0, .7);
animation: scanline 8s linear infinite;
}
.gloom::earlier than {
backdrop-filter: blur(10px);
opacity: .15;
}
.vignette::earlier than {
background-image: url("/img/crt.png");
background-size: 100% 100%;
remodel: scale(1.02);
opacity: .7;
}
I used the identical interval references to information the remainder of the design system. The first blue, #03049C, got here from the Blue Display screen of Dying. The gray, #BABABA, was chosen to resemble the plastic casing of previous Macintosh computer systems and workplace electronics.
For the hero, I needed one thing that felt like an operating-system wallpaper or startup display, which led to the cloud-flight WebGL scene. The floppy disk within the pricing part takes the thought of shopping for a design “bundle” actually, referencing the best way software program was as soon as bought bodily on a floppy disk.
Three.js: Three Scenes, Three Jobs
I constructed three separate Three.js scenes, every for a distinct a part of the location. The primary is the PX PUSH emblem. I loaded the SVG by way of SVGLoader, extruded it into geometry, added bevels, and utilized a metalness and clearcoat materials below PMREM surroundings lighting to create the mirrored chrome end.
Because the web page scrolls, the emblem scales down and strikes from the centre of the hero into the header. I additionally use scroll velocity to regulate its rotation. Scrolling quicker will increase the spin, whereas reversing the scroll route reverses the rotation. As soon as the emblem reaches the header, it turns into the hyperlink again to the homepage.
perform replace() {
const progress = gsap.utils.clamp(0, 1, window.scrollY / lockScrollY)
const scale = gsap.utils.interpolate(1, finalScale, progress)
const prime = Math.max(stickyTop, startTop - window.scrollY)
gsap.set(emblem, {
place: "fastened",
prime,
left: (window.innerWidth - startWidth) / 2,
scale,
transformOrigin: "50% 0%",
})
}
perform updateLogoScrollSpin(velocity) {
logoScrollSpinTarget = gsap.utils.clamp(
-logoScrollSpinMax,
logoScrollSpinMax,
-velocity * logoScrollSpinStrength
)
}
perform renderLogoFrame() {
logoScrollSpinTarget *= 0.9
logoScrollSpin += (logoScrollSpinTarget - logoScrollSpin) * 0.16
logoGroup.rotation.y += logoIdleSpin + logoScrollSpin
renderer.render(scene, digital camera)
}
The second scene is the cloud-flight hero. It makes use of roughly 8,000 cloud planes mixed right into a single buffer geometry, with a customized GLSL shader to fade their edges and add distance fog. There’s a hover easter egg within the hero copy: hovering over the phrase “velocity” raises the digital camera’s flight-speed multiplier from 0.2 to 2, then returns it to regular on mouse-out. It’s a small gesture — hover the phrase “velocity”, the scene will get quicker — and it was value the additional state monitoring only for that.
The third scene is the floppy disk within the pricing part. It makes use of a GLTF mannequin with its glossiness map recoloured at runtime on a canvas relatively than baked right into a static texture. The mannequin rotates and tilts with scroll progress, positioned beside the part that introduces the month-to-month design bundle.
All three scenes register themselves with the identical lifecycle system earlier than they begin loading, which is the place the engineering effort went.
The Founders Rotate Like a Character-Choose Display screen
For the About web page, we photographed every founder from fifteen totally different angles, styled as 80s company portraits. I related the picture sequence to a small scroll accumulator. Because the customer scrolls, the web page strikes forwards or backwards by way of the frames, making every individual seem to rotate in place. The impact works like an previous character-select display, however makes use of actual images as an alternative of a 3D mannequin.
onUpdate: (self) => {
const velocity = self.getVelocity()
imageAccumulator += Math.abs(velocity) * getImageCycleSpeed()
whereas (imageAccumulator >= 1) {
const route = velocity >= 0 ? 1 : -1
const pictures = getFounderImages(activeFounderIndex)
const subsequent = gsap.utils.wrap(
0,
pictures.size,
activeImageIndexes[activeFounderIndex] + route
)
gsap.set(".team__image", { autoAlpha: 0 })
gsap.set(pictures[next], { autoAlpha: 1 })
activeImageIndexes[activeFounderIndex] = subsequent
imageAccumulator -= 1
}
}
Every profile additionally carries a QR code generated at runtime, linking out to every founder’s personal website — styled with the identical gray and blue tokens as the remainder of the web page.
The Web page Waits for the Scenes
The principle engineering downside was coordinating three WebGL scenes with web page transitions. Nuxt’s web page hooks can inform me when a element has mounted, however not when a scene has loaded its property and rendered its first body. With out one other layer, the web page may reveal itself whereas one of many scenes was nonetheless clean.
I constructed a small lifecycle—ready → revealed → prepared—as a plain pub/sub system utilizing window occasions. Any element can register a promise by way of registerPageTask(). The web page waits till each registered process has settled, or till a tough timeout is reached:
const pendingTasks = new Set()
export perform registerPageTask(process) {
const pendingTask = Promise.resolve(process).catch(() => {})
pendingTasks.add(pendingTask)
pendingTask.lastly(() => pendingTasks.delete(pendingTask))
return () => pendingTasks.delete(pendingTask)
}
export perform waitForPageTasks() {
return Promise.allSettled([...pendingTasks])
}
The emblem, cloud hero, and floppy disk register themselves once they mount. This lets the web page watch for all three with out connecting the scenes straight to at least one one other.
A Drawer Fabricated from Paper
The Journal is an inventory of articles. As an alternative of opening every article as a standard new web page, I slide a drawer over the checklist and magnificence it like a sheet of workplace paper. A repeating paper-grain texture sits over a cardboard texture. The left edge makes use of a dwell CSS masks to create a vertical line of clear punch holes:
.journalDrawer__panel {
--hole-x: 2vw;
--hole-radius: .55vw;
--hole-gap: 50vh;
mask-image: radial-gradient(
circle at var(--hole-x) 50%,
clear 0 var(--hole-radius),
#000 calc(var(--hole-radius) + 1px)
);
mask-size: 100% var(--hole-gap);
mask-repeat: repeat-y;
}
The drawer has its personal Lenis occasion for inner scrolling. It waits for the web page lifecycle’s prepared occasion earlier than opening, and solely returns the path to /journal after the closing transition has completed. Every article nonetheless has its personal slug, canonical URL, Open Graph tags, and BlogPosting schema. The drawer adjustments how the article seems, however not how it’s routed or listed.
The Error Web page
The 404 and 500 pages recreate a traditional Blue Display screen of Dying utilizing a full-screen blue background, monospace sort, and duplicate based mostly on previous Home windows error screens. A line beneath reads “Press any key to proceed _” — urgent any key clears the error and returns the customer to the homepage. It additionally explains the origin of the location’s major blue with out including one other part or piece of explanatory copy.
The Marquee Titles Learn One Phrase at a Time
Each main part title makes use of the identical MarqueeText element: an outsized title repeated eight instances and shifting constantly throughout the display. Two scroll-based results run over the fixed motion. Because the title enters the viewport, its phrases seem individually in random order, rotating in from a slight 3D perspective:
gsap.fromTo(
phrases,
{
opacity: 0,
z: -100,
},
{
opacity: 1,
z: 0,
rotationX: 0,
stagger: {
every: 0.03,
from: "random",
},
scrollTrigger: {
set off: el,
begin: "prime 90%",
finish: "prime 0%",
scrub: true,
},
}
);
As the identical title approaches the highest of the viewport, it fades and blurs out:
gsap.fromTo(
el,
{
opacity: 1,
filter: "blur(0px)",
},
{
opacity: 0,
filter: "blur(20px)",
scrollTrigger: {
set off: el,
begin: "prime 5%",
finish: "prime -30%",
scrub: true,
},
}
);
I related each results by way of a small declarative attribute system. Any ingredient can obtain an attribute similar to effect__titleRandom or effect__fadeOut, and one perform finds these parts and attaches the related GSAP timeline after every web page hundreds.
The marquee additionally makes use of effect__separatorIn to disclose its prime and backside guidelines with a clip-path animation. The identical system handles textual content fades, character reveals, parallax, and different transitions throughout the location.
Bringing the ’80s Workplace into the Browser
The goal was to not recreate an previous web site, however to deliver the visible language of Nineteen Eighties company tradition into a contemporary digital expertise. The CRT overlays, chrome emblem, cloud-flight scene, floppy disk, Blue Display screen of Dying reference, and the Journal paper drawer with punch holes, all come from the identical interval, however are constructed with a present Nuxt, Three.js, and GSAP stack. Retaining these references constant throughout the location helped the person results really feel like components of the identical system relatively than separate visible concepts.
Credit
Design, idea, and improvement — Stud Artistic Home (stud.home), directed by Lewis Webber (wbbr.co), in collaboration with the PX PUSH workforce.









