1:"$Sreact.fragment" 2:I[65875,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js"],"ThemeProvider"] 3:I[39756,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/247eb132b7f7b574.js"],"default"] 4:I[37457,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/247eb132b7f7b574.js"],"default"] 5:I[71311,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js"],"FloatingActions"] 6:I[20158,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js"],"Analytics"] 7:I[34863,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js"],"Toaster"] 8:I[79799,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js"],"ModalProvider"] 9:I[3995,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js","/_next/static/chunks/95a5e382a1e8200a.js","/_next/static/chunks/06fe5961b16f81d0.js","/_next/static/chunks/8c955ce35598c7b8.js"],"MainNav"] 10:I[68027,[],"default"] :HL["/_next/static/chunks/98b318e576119eda.css","style"] :HL["/_next/static/chunks/9f6b77641f836b5a.css","style"] :HL["/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/CalSans_SemiBold-s.p.4d9d14e3.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/f6a709d5935db663-s.p.529c3631.woff2","font",{"crossOrigin":"","type":"font/woff2"}] 0:{"P":null,"b":"pOVubU_Kt6m3SrfLgqzM-","c":["","blogs","3d-card-threejs-blender"],"q":"","i":false,"f":[[["",{"children":["(root)",{"children":["blogs",{"children":[["slug","3d-card-threejs-blender","d"],{"children":["__PAGE__",{}]}]}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/98b318e576119eda.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/0a3f498ef71c618e.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/61bc785dc6bd3109.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/_next/static/chunks/a676f0ced11453e9.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/_next/static/chunks/4b778b2e48718709.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":[["$","head",null,{"children":[["$","link",null,{"rel":"icon","href":"/portfolio/logos/logo-3.png","media":"(prefers-color-scheme: light)"}],["$","link",null,{"rel":"icon","href":"/portfolio/logos/logo-4.png","media":"(prefers-color-scheme: dark)"}]]}],["$","body",null,{"suppressHydrationWarning":true,"className":"min-h-screen bg-background font-sans antialiased inter_dd475d0c-module__9A3jha__variable fontheading_82debfef-module__MkcH8W__variable","children":["$","$L2",null,{"attribute":"class","defaultTheme":"dark","enableSystem":false,"themes":["light","dark","retro","cyberpunk","paper","aurora","synthwave"],"children":[["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}],["$","$L5",null,{}],["$","$L6",null,{}],["$","$L7",null,{}],["$","$L8",null,{}]]}]}],null]}]]}],{"children":[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/9f6b77641f836b5a.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/95a5e382a1e8200a.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/06fe5961b16f81d0.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/_next/static/chunks/8c955ce35598c7b8.js","async":true,"nonce":"$undefined"}]],["$","div",null,{"className":"flex min-h-screen flex-col bg-[#020617]","children":[["$","header",null,{"className":"sticky top-0 z-50 border-b border-white/10 bg-[#020617]/90 backdrop-blur","children":["$","div",null,{"className":"container flex h-20 items-center justify-between py-6","children":[["$","$L9",null,{"items":[{"title":"Home","href":"/"},{"title":"Projects","href":"/projects"},{"title":"Impact","href":"/#impact"},{"title":"Contact","href":"/#contact"}]}],["$","a",null,{"href":"/#contact","className":"hidden rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-blue-500 md:inline-flex","children":"Let's Talk"}]]}]}],["$","main",null,{"className":"container flex-1","children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:1:props:children:props:children:0:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],"$La","$Lb"]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}],{"children":["$Lc",{"children":["$Ld",{"children":["$Le",{},null,false,false]},null,false,false]},null,false,false]},null,false,false]},null,false,false],"$Lf",false]],"m":"$undefined","G":["$10",[]],"S":true} 12:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/247eb132b7f7b574.js"],"OutletBoundary"] 13:"$Sreact.suspense" 15:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/247eb132b7f7b574.js"],"ViewportBoundary"] 17:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/247eb132b7f7b574.js"],"MetadataBoundary"] a:["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:1:props:style","children":404}] b:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}] c:["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] d:["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] e:["$","$1","c",{"children":["$L11",[["$","script","script-0",{"src":"/_next/static/chunks/15c613f0f258455e.js","async":true,"nonce":"$undefined"}]],["$","$L12",null,{"children":["$","$13",null,{"name":"Next.MetadataOutlet","children":"$@14"}]}]]}] f:["$","$1","h",{"children":[null,["$","$L15",null,{"children":"$@16"}],["$","div",null,{"hidden":true,"children":["$","$L17",null,{"children":["$","$13",null,{"name":"Next.Metadata","children":"$@18"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] 19:I[88754,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js","/_next/static/chunks/95a5e382a1e8200a.js","/_next/static/chunks/06fe5961b16f81d0.js","/_next/static/chunks/8c955ce35598c7b8.js","/_next/static/chunks/15c613f0f258455e.js"],"ClientPageWrapper"] 1a:I[79520,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js","/_next/static/chunks/95a5e382a1e8200a.js","/_next/static/chunks/06fe5961b16f81d0.js","/_next/static/chunks/8c955ce35598c7b8.js","/_next/static/chunks/15c613f0f258455e.js"],""] 1c:I[73552,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js","/_next/static/chunks/95a5e382a1e8200a.js","/_next/static/chunks/06fe5961b16f81d0.js","/_next/static/chunks/8c955ce35598c7b8.js","/_next/static/chunks/15c613f0f258455e.js"],"AnimatedText"] 1d:I[22016,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js","/_next/static/chunks/95a5e382a1e8200a.js","/_next/static/chunks/06fe5961b16f81d0.js","/_next/static/chunks/8c955ce35598c7b8.js","/_next/static/chunks/15c613f0f258455e.js"],""] 1e:I[3601,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js","/_next/static/chunks/95a5e382a1e8200a.js","/_next/static/chunks/06fe5961b16f81d0.js","/_next/static/chunks/8c955ce35598c7b8.js","/_next/static/chunks/15c613f0f258455e.js"],"AnimatedSection"] 1b:T488,{"@context":"https://schema.org","@type":"BlogPosting","headline":"3D Card with Embedded Links Using Three.js and Blender","description":"Ever thought your traditional business card could use a serious upgrade? Here's how I used Three.js and Blender to transform a plain business card into an interactive 3D masterpiece with embedded clickable links.","datePublished":"2023-11-01T00:00:00.000Z","dateModified":"2023-11-01T00:00:00.000Z","author":{"@type":"Person","name":"Hussein Maghrabi","url":"https://h-maghrabi.tech/","sameAs":["https://www.linkedin.com/in/hussein-maghrabi/","https://www.linkedin.com/in/hussein-maghrabi/"]},"publisher":{"@type":"Person","name":"Hussein Maghrabi","url":"https://h-maghrabi.tech/"},"url":"https://h-maghrabi.tech//blogs/3d-card-threejs-blender","mainEntityOfPage":{"@type":"WebPage","@id":"https://h-maghrabi.tech//blogs/3d-card-threejs-blender"},"image":"https://h-maghrabi.tech//projects/card/card_2.webp","keywords":"Three.js, Blender, 3D, JavaScript, WebGL","wordCount":914,"timeRequired":"PT7M","inLanguage":"en-US","isPartOf":{"@type":"Blog","name":"Hussein Maghrabi's Blog","url":"https://h-maghrabi.tech//blogs"}}11:["$","$L19",null,{"children":[["$","$L1a",null,{"id":"schema-blog-post","type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"$1b"}}],["$","$L1a",null,{"id":"schema-breadcrumb-post","type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https://h-maghrabi.tech/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blogs\",\"item\":\"https://h-maghrabi.tech//blogs\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"3D Card with Embedded Links Using Three.js and Blender\",\"item\":\"https://h-maghrabi.tech//blogs/3d-card-threejs-blender\"}]}"}}],["$","article",null,{"className":"max-w-3xl mx-auto px-4 sm:px-6 py-8","children":[["$","$L1c",null,{"delay":0,"children":["$","nav",null,{"aria-label":"Breadcrumb","className":"mb-6","children":["$","ol",null,{"className":"flex items-center gap-1.5 text-sm text-muted-foreground","children":[["$","li",null,{"children":["$","$L1d",null,{"href":"/","className":"hover:text-foreground transition-colors","children":"Home"}]}],["$","li",null,{"aria-hidden":"true","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-chevron-right w-3.5 h-3.5","children":[["$","path","mthhwq",{"d":"m9 18 6-6-6-6"}],"$undefined"]}]}],["$","li",null,{"children":["$","$L1d",null,{"href":"/blogs","className":"hover:text-foreground transition-colors","children":"Blogs"}]}],["$","li",null,{"aria-hidden":"true","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-chevron-right w-3.5 h-3.5","children":[["$","path","mthhwq",{"d":"m9 18 6-6-6-6"}],"$undefined"]}]}],["$","li",null,{"className":"text-foreground font-medium truncate max-w-[200px] sm:max-w-[300px]","aria-current":"page","children":"3D Card with Embedded Links Using Three.js and Blender"}]]}]}]}],["$","$L1e",null,{"direction":"up","children":["$","header",null,{"className":"mb-8","children":[["$","div",null,{"className":"flex flex-wrap gap-2 mb-4","children":[["$","span","Three.js",{"className":"inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-primary/10 text-primary border border-primary/20","children":"Three.js"}],["$","span","Blender",{"className":"inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-primary/10 text-primary border border-primary/20","children":"Blender"}],"$L1f","$L20","$L21"]}],"$L22","$L23","$L24"]}]}],"$L25","$L26","$L27"]}]]}] 28:I[85437,["/_next/static/chunks/0a3f498ef71c618e.js","/_next/static/chunks/61bc785dc6bd3109.js","/_next/static/chunks/a676f0ced11453e9.js","/_next/static/chunks/4b778b2e48718709.js","/_next/static/chunks/95a5e382a1e8200a.js","/_next/static/chunks/06fe5961b16f81d0.js","/_next/static/chunks/8c955ce35598c7b8.js","/_next/static/chunks/15c613f0f258455e.js"],"Image"] 1f:["$","span","3D",{"className":"inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-primary/10 text-primary border border-primary/20","children":"3D"}] 20:["$","span","JavaScript",{"className":"inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-primary/10 text-primary border border-primary/20","children":"JavaScript"}] 21:["$","span","WebGL",{"className":"inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-primary/10 text-primary border border-primary/20","children":"WebGL"}] 22:["$","h1",null,{"className":"font-heading text-3xl sm:text-4xl md:text-5xl leading-tight text-foreground mb-4","children":"3D Card with Embedded Links Using Three.js and Blender"}] 23:["$","p",null,{"className":"text-lg text-muted-foreground leading-relaxed mb-6","children":"Ever thought your traditional business card could use a serious upgrade? Here's how I used Three.js and Blender to transform a plain business card into an interactive 3D masterpiece with embedded clickable links."}] 24:["$","div",null,{"className":"flex flex-wrap items-center gap-4 text-sm text-muted-foreground pb-6 border-b border-border","children":[["$","address",null,{"className":"flex items-center gap-1.5 not-italic","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-user w-4 h-4","children":[["$","path","975kel",{"d":"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"}],["$","circle","17ys0d",{"cx":"12","cy":"7","r":"4"}],"$undefined"]}],["$","a",null,{"rel":"author","href":"https://h-maghrabi.tech/","className":"hover:text-foreground transition-colors","children":"Hussein Maghrabi"}]]}],["$","time",null,{"dateTime":"2023-11-01T00:00:00.000Z","className":"flex items-center gap-1.5","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-calendar w-4 h-4","children":[["$","path","1cmpym",{"d":"M8 2v4"}],["$","path","4m81vk",{"d":"M16 2v4"}],["$","rect","1hopcy",{"width":"18","height":"18","x":"3","y":"4","rx":"2"}],["$","path","8toen8",{"d":"M3 10h18"}],"$undefined"]}],"November 1, 2023"]}],["$","span",null,{"className":"flex items-center gap-1.5","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-clock w-4 h-4","children":[["$","circle","1mglay",{"cx":"12","cy":"12","r":"10"}],["$","polyline","68esgv",{"points":"12 6 12 12 16 14"}],"$undefined"]}],7," min read"]}]]}] 25:["$","$L1e",null,{"direction":"up","delay":0.05,"children":["$","figure",null,{"className":"mb-10","children":["$","$L28",null,{"src":"/projects/card/card_2.webp","alt":"3D Card with Embedded Links Using Three.js and Blender","width":768,"height":400,"className":"w-full h-auto rounded-lg border border-border object-cover","priority":true}]}]}] 29:T252e,

Ever thought your traditional business card could use a serious upgrade? Well, I sure did! Buckle up because I'm about to take you on a mind-bending journey into the world of creativity, innovation, and 3D magic. Welcome to the behind-the-scenes tale of my Dynamic Portfolio Card project โ€” where Three.js meets Blender to transform a plain ol' business card into an interactive work of art. ๐Ÿš€โœจ

Live Demo: card.nbarkiya.xyz ยท Source Code: GitHub


Let's Jump Right In

Here's a step-by-step guide on how to create this project.

Step 1 โ€” Setting Up the Project

We'll use ViteJS with the Vanilla JS template for a fast, modern dev environment:

npm create vite@latest my-3d-card -- --template vanilla

Step 2 โ€” Installing Essential Libraries

We need two packages: gsap for smooth animations and three for the 3D engine.

npm i three gsap

Step 3 โ€” Creating the HTML Canvas

Set up your index.html file. This is the canvas for the 3D scene and links to our script:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/logo.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Card | Naman Barkiya</title>
  </head>
  <body>
    <canvas class="webgl"></canvas>
    <script type="module" src="./main.js"></script>
  </body>
</html>

Moving Forward with main.js

Importing Packages

import "./card-style.css";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

Setting Up the Scene and Lighting

A good 3D scene needs layered lighting to look realistic. We set up a front light, back light, mid light and a PointLight:

const scene = new THREE.Scene();
const sizes = { width: window.innerWidth, height: window.innerHeight };

const lightFront = new THREE.DirectionalLight(0xffffff, 0.7);
lightFront.position.set(0, 10, 30);
scene.add(lightFront);

const lightBack = new THREE.DirectionalLight(0xffffff, 0.7);
lightBack.position.set(-30, 10, -30);
scene.add(lightBack);

const lightMid = new THREE.DirectionalLight(0xffffff, 0.7);
lightMid.position.set(30, 10, -30);
scene.add(lightMid);

const pointLight = new THREE.PointLight(0xffffff, 1, 60);
pointLight.position.set(10, 10, 30);
scene.add(pointLight);

const camera = new THREE.PerspectiveCamera(45, sizes.width / sizes.height);
camera.position.z = 30;
scene.add(camera);

const canvas = document.querySelector(".webgl");
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(sizes.width, sizes.height);
renderer.setClearColor(0x030712, 1);
renderer.setPixelRatio(2);

Building the 3D Card in Blender

Before jumping back into the JavaScript, we need the actual 3D model:

  1. Download the blend file from Google Drive assets
  2. Customize your card โ€” use the Figma template to design your own card art
  3. In Blender, replace card.png and logo.png in the Shading section with your files
  4. Adjust dimensions in the UV Editing Section
  5. Export the model as glTF 2.0 (.glb) format

Loading the Model and Embedding Links

The key idea โ€” we define invisible clickable planes/circles that are positioned precisely over the areas on the card where links should exist.

Define Link Positions

const linkPos = {
  box1: {
    x: 0.7,
    y: 1.21,
    z: 0.03,
    name: "ClickableBox1",
    link: "/naman_barkiya_resume.pdf",
  },
  box2: {
    x: 0.06,
    y: -0.4,
    z: 0.03,
    name: "ClickableBox2",
    link: "https://nbarkiya.xyz",
  },
  circle1: {
    x: -0.46,
    y: -1.06,
    z: 0.03,
    name: "ClickableCircle1",
    link: "https://github.com/namanbarkiya",
  },
  circle2: {
    x: 0.05,
    y: -1.06,
    z: 0.03,
    name: "ClickableCircle2",
    link: "https://www.linkedin.com/in/naman-barkiya-015323200/",
  },
  circle3: {
    x: 0.55,
    y: -1.06,
    z: 0.03,
    name: "ClickableCircle3",
    link: "mailto:naman.barkiya02@gmail.com",
  },
};

Load the Model and Attach Invisible Hit Areas

const loader = new GLTFLoader();
let mesh;

loader.load("/naman_card.glb", (gltf) => {
  mesh = gltf.scene;
  mesh.traverse((child) => {
    if (child.isMesh) child.name = "ClickablePart1";
  });

  const scaleFactor = 5;
  mesh.scale.set(scaleFactor, scaleFactor, scaleFactor);
  scene.add(mesh);

  // Invisible box over the resume link area
  const box1 = new THREE.Mesh(
    new THREE.PlaneGeometry(0.5, 0.08),
    new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })
  );
  box1.position.set(linkPos.box1.x, linkPos.box1.y, linkPos.box1.z);
  box1.name = linkPos.box1.name;
  mesh.add(box1);

  // Invisible circle over the GitHub icon
  const circle1 = new THREE.Mesh(
    new THREE.CircleGeometry(0.16, 32),
    new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })
  );
  circle1.position.set(linkPos.circle1.x, linkPos.circle1.y, linkPos.circle1.z);
  circle1.name = linkPos.circle1.name;
  mesh.add(circle1);

  // ... repeat for circle2, circle3, box2

  mesh.rotation.y = 0;
  mesh.rotation.z = 0;
  loop();
});

Controls: Orbit and Auto-Rotate

const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.enablePan = false;
controls.enableZoom = false;
controls.autoRotate = false;
controls.minPolarAngle = 1.5;
controls.maxPolarAngle = 1.5;
controls.autoRotateSpeed = 3;

Making It Responsive

window.addEventListener("resize", () => {
  sizes.height = window.innerHeight;
  sizes.width = window.innerWidth;
  camera.aspect = sizes.width / sizes.height;
  camera.updateProjectionMatrix();
  renderer.setSize(sizes.width, sizes.height);
});

const loop = () => {
  controls.update();
  renderer.render(scene, camera);
  window.requestAnimationFrame(loop);
};

Raycasting โ€” Opening Links on Click

This is the secret sauce. We use a THREE.Raycaster to check if a click intersects with any of the invisible hit meshes, then open the corresponding link:

let autoRotate = false;

canvas.addEventListener("click", (event) => {
  controls.autoRotate = !autoRotate;
  autoRotate = !autoRotate;

  const mouse = {
    x: (event.clientX / sizes.width) * 2 - 1,
    y: -(event.clientY / sizes.height) * 2 + 1,
  };

  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(mouse, camera);

  // Check each clickable object and open its link
  ["circle1", "circle2", "circle3", "box1", "box2"].forEach((key) => {
    const obj = scene.getObjectByName(linkPos[key].name);
    if (obj && raycaster.intersectObject(obj).length > 0) {
      window.open(linkPos[key].link, "_blank");
    }
  });
});

Styles (card-style.css)

:root {
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;
  -webkit-font-smoothing: antialiased;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
}

.clickable-box {
  cursor: pointer;
}

Conclusion

Well folks, that's a wrap! We've gone from a plain business card to a jaw-dropping 3D Portfolio Card with embedded links โ€” all using the power of Three.js and Blender. The invisible raycasting approach for links is the key insight: you can overlay any clickable geometry on your 3D model without modifying the model itself.

Grab the assets, customize the card with your own links and branding, and ship something that'll genuinely wow people.


Useful Links

26:["$","$L1e",null,{"direction":"up","delay":0.1,"children":["$","section",null,{"className":"blog-content","dangerouslySetInnerHTML":{"__html":"$29"}}]}] 27:["$","$L1e",null,{"direction":"up","delay":0.15,"className":"mt-16 pt-8 border-t border-border","children":["$","footer",null,{"className":"flex items-center justify-between","children":[["$","$L1d",null,{"href":"/blogs","className":"inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2 rounded-lg gap-2","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-chevron-left w-4 h-4","children":[["$","path","1wnfg3",{"d":"m15 18-6-6 6-6"}],"$undefined"]}],"All posts"]}],["$","div",null,{"className":"text-sm text-muted-foreground","children":["Written by"," ",["$","$L1d",null,{"href":"https://www.linkedin.com/in/hussein-maghrabi/","target":"_blank","rel":"noopener noreferrer","className":"font-medium text-foreground hover:text-primary transition-colors","children":"Hussein Maghrabi"}]]}]]}]}] 16:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] 2a:I[27201,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/247eb132b7f7b574.js"],"IconMark"] 18:[["$","title","0",{"children":"3D Card with Embedded Links Using Three.js and Blender | Hussein Maghrabi - Senior PHP / Laravel Developer"}],["$","meta","1",{"name":"description","content":"Ever thought your traditional business card could use a serious upgrade? Here's how I used Three.js and Blender to transform a plain business card into an interactive 3D masterpiece with embedded clickable links."}],["$","link","2",{"rel":"author","href":"https://h-maghrabi.tech/"}],["$","meta","3",{"name":"author","content":"Hussein Maghrabi"}],["$","link","4",{"rel":"manifest","href":"/manifest.webmanifest","crossOrigin":"$undefined"}],["$","meta","5",{"name":"keywords","content":"Three.js,Blender,3D,JavaScript,WebGL"}],["$","meta","6",{"name":"creator","content":"husseinmaghrabi"}],["$","meta","7",{"name":"robots","content":"index, follow, max-image-preview:large, max-snippet:-1"}],["$","link","8",{"rel":"canonical","href":"https://h-maghrabi.tech//blogs/3d-card-threejs-blender"}],["$","meta","9",{"property":"og:title","content":"3D Card with Embedded Links Using Three.js and Blender"}],["$","meta","10",{"property":"og:description","content":"Ever thought your traditional business card could use a serious upgrade? Here's how I used Three.js and Blender to transform a plain business card into an interactive 3D masterpiece with embedded clickable links."}],["$","meta","11",{"property":"og:url","content":"https://h-maghrabi.tech//blogs/3d-card-threejs-blender"}],["$","meta","12",{"property":"og:site_name","content":"Hussein Maghrabi - Senior PHP / Laravel Developer"}],["$","meta","13",{"property":"og:image","content":"https://h-maghrabi.tech//projects/card/card_2.webp"}],["$","meta","14",{"property":"og:image:width","content":"1200"}],["$","meta","15",{"property":"og:image:height","content":"630"}],["$","meta","16",{"property":"og:image:alt","content":"3D Card with Embedded Links Using Three.js and Blender"}],["$","meta","17",{"property":"og:type","content":"article"}],["$","meta","18",{"property":"article:published_time","content":"2023-11-01"}],["$","meta","19",{"property":"article:modified_time","content":"2023-11-01"}],["$","meta","20",{"property":"article:author","content":"Hussein Maghrabi"}],["$","meta","21",{"property":"article:tag","content":"Three.js"}],["$","meta","22",{"property":"article:tag","content":"Blender"}],["$","meta","23",{"property":"article:tag","content":"3D"}],["$","meta","24",{"property":"article:tag","content":"JavaScript"}],["$","meta","25",{"property":"article:tag","content":"WebGL"}],["$","meta","26",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","27",{"name":"twitter:creator","content":"@husseinmaghrabi"}],["$","meta","28",{"name":"twitter:title","content":"3D Card with Embedded Links Using Three.js and Blender"}],["$","meta","29",{"name":"twitter:description","content":"Ever thought your traditional business card could use a serious upgrade? Here's how I used Three.js and Blender to transform a plain business card into an interactive 3D masterpiece with embedded clickable links."}],["$","meta","30",{"name":"twitter:image","content":"https://h-maghrabi.tech//projects/card/card_2.webp"}],["$","link","31",{"rel":"shortcut icon","href":"/portfolio/logos/logo-3.png"}],["$","link","32",{"rel":"icon","href":"/portfolio/logos/logo-3.png"}],["$","link","33",{"rel":"apple-touch-icon","href":"/portfolio/logos/logo-3.png"}],["$","$L2a","34",{}]] 14:null