Recently, 3D graphics are used in web app development. In particular, React Three Fiber (R3F) allows you to easily handle Three.js as a React component.
This time, we will also utilize Drei implement colorful "PokePoke" 3D backgrounds using React + TypeScript
📌 What's more, this time we won't use any special 3D models!
This is how expressing it can be done using only the basic objects (Box, Plane, etc.) provided as standard in Three.js We'll have you experience this.
💡 Completed image
👇 Create a 3D background 👇
📺 Watch the demo on YouTube : You can watch it from this link

✅
with colorful cards placed in a circular shape✅
that utilizes specular reflections✅ A UI that combines HTML/CSS
🚀 Let's take a look at the point that "no special 3D models required! You can do this with just standard objects!"
Now let's actually write some code!
We will continue to create lessons and works from TypeScript x React Three Fiber in the future!
We will be making an announcement on YouTube, so please subscribe to our YouTube channel and wait for notifications!
📺 Watch YouTube : You can watch it from this link
If you'd like to know what React Three Fiber can do, please refer to the following!
We have easy-to-use works available!
- I tried making bears walk with React x Three.js!
- I tried making an old man dance on React x Three.js!
- I tried to display a 3D model with React x Three.js!
- I made a 3D button that explodes in React x Three.js!
- React Three Fiber x Drei x Introduction to TypeScript! Poke Poke style 3D background made with standard objects!
🚀Introduction to technical elements: Tools and libraries to use in this project
You can change the tools and libraries you use to one that is easy to use for yourself, but this project will explain this assumption.
- VSCode
-
- A free code editor provided by Microsoft.
- It doesn't need to be VSCode, but there are many extensions, so I recommend it.
- It is also recommended to include ESLint or Prettier.
- Node.js
-
- A JavaScript built on Chrome's V8 JavaScript engine .
- You can run JavaScript code outside of your browser.
- This is explained based on the assumption that it is already installed, so please download it from
https://nodejs.org/ja *We recommend downloading the long-term stable version of LTS.
- Vite
-
- A build tool for modern web projects. It is characterized by its fast and lightweight
- The previously used "CRA (create-react-app)" is not listed on the official website, and has become an old technology.
- From now on, Vite should be the best option when creating apps with React.
- React
-
- This is a JavaScript library for building a UI (user interface). It was developed by Facebook and is still used in many web apps today.
- Three.js
-
- A JavaScript library for easily creating 3D graphics. It abstracts the complex operations of WebGL and enables intuitive 3D development.
- It's easy to create 3D graphics and is easier to use than direct WebGL operations.
- React Three Fiber
-
- This is a library that allows Three.js to be used with React. It combines React's component structure with Three.js' 3D engine.
- Three.js can be used in the React development style, allowing for intuitive and efficient development.
- React Three Drei
-
- A collection of useful utility components for React Three Fiber. It's easy to add the commonly used Three.js features.
- Complex Three.js features can be achieved with short code, reducing learning costs.
🚀 Create a "PokePoke-style 3D background" with React Three Fiber × Drei × TypeScript!
📌The environment construction is explained in this article

📌This time, we will not use any special 3D models, but only use standard objects that are available in React Three Fiber and React Three Drei.
If you would like to learn more about standard objects, please also refer to this article

Please check GitHub for the complete source code you created this time.
💾 GitHub Repository : Check the source code at this link
💡Create a login form!!
First, simply create a login form with React.
We use tailwind-css as the CSS library.
// === Main App Component === const LoginScreen = () => { return ( <> {/* LoginForm */} <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white/90 rounded-lg p-8 shadow-lg text-center"><h2 className="text-2xl font-semibold text-gray-700 mb-6"> Login </h2><form><div className="mb-4"><input type="text" placeholder="ユーザー名" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div><div className="mb-6"><input type="password" placeholder="パスワード" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div> <button type="submit" className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" >Login</button></form></div></> ); }; export default LoginScreen;

💡Show Canvas in the background!!
Place your Canvas first and have a camera and lighting ready.
import { Canvas } from "@react-three/fiber"; import { OrbitControls, } from "@react-three/drei"; // === Main App Component === const LoginScreen = () => { return ( <> {/* Canvas in Three.js */}<Canvas camera={{ position: [0, 0, 10], fov: 50 }}><color attach="background" args={["white"]} /> {/* Writing */}<ambientLight intensity={10} /><directionalLight position={[0, 5, 0]} intensity={15} /> {/* Camera operation */} <OrbitControls enablePan={false} enableZoom={false} minPolarAngle={Math.PI / 2} maxPolarAngle={Math.PI / 2} /></Canvas> {/* Login form created using HTML/CSS */} <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white/90 rounded-lg p-8 shadow-lg text-center"><h2 className="text-2xl font-semibold text-gray-700 mb-6"> Login </h2><form><div className="mb-4"><input type="text" placeholder="ユーザー名" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div><div className="mb-6"><input type="password" placeholder="パスワード" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div> <button type="submit" className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" >Login</button></form></div></> ); }; export default LoginScreen;

💡Place of card pack objects!!
Next, place an object that looks like a Poke Poke card pack in the background.
With this alone, doesn't it look like the screen to select a Poke Poke pack? (lol)
import { Canvas } from "@react-three/fiber"; import { MeshReflectorMaterial, OrbitControls, } from "@react-three/drei"; // === Place a box inspired by a card pack in a circle, centered around [0,0,0] === const CircularCardPack = ({ radius = 5, count = 10, cardSize = [1, 1.5, 0.1], // Width, Height, Depth }: { radius?: number; // radius of the circle count?: number; // Number of cards to place cardSize?: [number, number, number]; // Card size }) => { const cards = Array.from({ length: count }); // Create array return (<group> {cards.map((_, i) => { const angle = (i / count) * Math.PI * 2; // Angle on the circumference const x = radius * Math.cos(angle); // X coordinate const z = radius * Math.sin(angle); // Z coordinate const rotationY = -angle + Math.PI / 2; // Rotate the card facing the center return ( <mesh key={i} position={[x, -1, z]} // カードの位置 rotation={[0, rotationY, 0]} // カードの向き ><boxGeometry args={cardSize} /><meshStandardMaterial color={`hsl(${(i / count) * 360}, 70%, 50%)`} /></mesh> ); })}</group> ); }; // === Main App Component === const LoginScreen = () => { return ( <> {/* Canvas in Three.js */}<Canvas camera={{ position: [0, 0, 10], fov: 50 }}><color attach="background" args={["white"]} /> {/* Writing */}<ambientLight intensity={10} /><directionalLight position={[0, 5, 0]} intensity={15} /> {/* Camera operation */} <OrbitControls enablePan={false} enableZoom={false} minPolarAngle={Math.PI / 2} maxPolarAngle={Math.PI / 2} /> {/* Card Object */}<CircularCardPack /></Canvas> {/* Login form created using HTML/CSS */} <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white/90 rounded-lg p-8 shadow-lg text-center"><h2 className="text-2xl font-semibold text-gray-700 mb-6"> Login </h2><form><div className="mb-4"><input type="text" placeholder="ユーザー名" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div><div className="mb-6"><input type="password" placeholder="パスワード" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div> <button type="submit" className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" >Login</button></form></div></> ); }; export default LoginScreen;

💡 Place a mirror surface under the pack!!
When I checked where I chose Poke Poke packs, the packs would appear to be reflective, so I tried adding a mirror surface under the pack in the same way.
import { Canvas } from "@react-three/fiber"; import { MeshReflectorMaterial, OrbitControls, } from "@react-three/drei"; // === mirror components === const ReflectivePlane = () => { return ( <><mesh rotation-x={-Math.PI / 2} position={[0, -2, 0]}><planeGeometry args={[100, 100]} /> {/* Larger mirror to cover the entire background */} <MeshReflectorMaterial blur={[200, 100]} mixBlur={0.7} mixStrength={1} mixContrast={1} resolution={1024} mirror={1} depthScale={0.1} reflectorOffset={0.2} /></mesh></> ); }; // === Place a box inspired by a card pack in a circle with the center of [0,0,0] === const CircularCardPack = ({ radius = 5, count = 10, cardSize = [1, 1.5, 0.1], // Width, height, depth }: { radius?: number; // radius of circle count?: number; // Number of cards to place cardSize?: [number, number, number]; // Card size }) => { const cards = Array.from({ length: count }); // Create an array return (<group> {cards.map((_, i) => { const angle = (i / count) * Math.PI * 2; // Angle on the circumference const x = radius * Math.cos(angle); // X coordinate const z = radius * Math.sin(angle); // Z coordinate const rotationY = -angle + Math.PI / 2; // Rotate the card facing the center return ( <mesh key={i} position={[x, -1, z]} // カードの位置 rotation={[0, rotationY, 0]} // カードの向き ><boxGeometry args={cardSize} /><meshStandardMaterial color={`hsl(${(i / count) * 360}, 70%, 50%)`} /></mesh> ); })}</group> ); }; // === Main App Component === const LoginScreen = () => { return ( <> {/* Canvas in Three.js */}<Canvas camera={{ position: [0, 0, 10], fov: 50 }}><color attach="background" args={["white"]} /> {/* Writing */}<ambientLight intensity={10} /><directionalLight position={[0, 5, 0]} intensity={15} /> {/* Camera operation */} <OrbitControls enablePan={false} enableZoom={false} minPolarAngle={Math.PI / 2} maxPolarAngle={Math.PI / 2} /> {/* Cardpack object */}<CircularCardPack /> {/* Mirror object */}<ReflectivePlane /></Canvas> {/* Login form created using HTML/CSS */} <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white/90 rounded-lg p-8 shadow-lg text-center"><h2 className="text-2xl font-semibold text-gray-700 mb-6"> Login </h2><form><div className="mb-4"><input type="text" placeholder="ユーザー名" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div><div className="mb-6"><input type="password" placeholder="パスワード" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div> <button type="submit" className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" >Login</button></form></div></> ); }; export default LoginScreen;

💡Added background!!
This doesn't need it, but I was a little lonely so I'll add a background too.
The background is also standard from React Three Drei.
import { Canvas } from "@react-three/fiber"; import { Environment, MeshReflectorMaterial, OrbitControls, } from "@react-three/drei"; // === Const ReflectivePlane = () => { return ( <><mesh rotation-x={-Math.PI / 2} position={[0, -2, 0]}><planeGeometry args={[100, 100]} /> {/* Larger mirror to cover the entire background */} <MeshReflectorMaterial blur={[200, 100]} mixBlur={0.7} mixStrength={1} mixContrast={1} resolution={1024} mirror={1} depthScale={0.1} reflectorOffset={0.2} /></mesh></> ); }; // === Place a box inspired by a card pack in a circle with the center of [0,0,0] === const CircularCardPack = ({ radius = 5, count = 10, cardSize = [1, 1.5, 0.1], // Width, height, depth }: { radius?: number; // radius of circle count?: number; // Number of cards to place cardSize?: [number, number, number]; // Card size }) => { const cards = Array.from({ length: count }); // Create an array return (<group> {cards.map((_, i) => { const angle = (i / count) * Math.PI * 2; // Angle on the circumference const x = radius * Math.cos(angle); // X coordinate const z = radius * Math.sin(angle); // Z coordinate const rotationY = -angle + Math.PI / 2; // Rotate the card facing the center return ( <mesh key={i} position={[x, -1, z]} // カードの位置 rotation={[0, rotationY, 0]} // カードの向き ><boxGeometry args={cardSize} /><meshStandardMaterial color={`hsl(${(i / count) * 360}, 70%, 50%)`} /></mesh> ); })}</group> ); }; // === Main App Component === const LoginScreen = () => { return ( <> {/* Canvas in Three.js */}<Canvas camera={{ position: [0, 0, 10], fov: 50 }}><color attach="background" args={["white"]} /> {/* Writing */}<ambientLight intensity={10} /><directionalLight position={[0, 5, 0]} intensity={15} /> {/* Camera operation */} <OrbitControls enablePan={false} enableZoom={false} minPolarAngle={Math.PI / 2} maxPolarAngle={Math.PI / 2} /> {/* Cardpack object */}<CircularCardPack /> {/* Mirror object */}<ReflectivePlane /> {/* Background */}<Environment preset="night" background /></Canvas> {/* Login form created using HTML/CSS */} <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white/90 rounded-lg p-8 shadow-lg text-center"><h2 className="text-2xl font-semibold text-gray-700 mb-6"> Login </h2><form><div className="mb-4"><input type="text" placeholder="ユーザー名" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div><div className="mb-6"><input type="password" placeholder="パスワード" className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /></div> <button type="submit" className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" >Login</button></form></div></> ); }; export default LoginScreen;

lastly
This time, we've reproduced the PokePoke pack selection screen using only standard objects, as a "PokePoke style 3D background made from standard objects!"
📌You can get a detailed understanding of the actual movements by watching the YouTube below!
📺 Watch the demo on YouTube : You can watch it from this link

📌The code I created this time is posted on GitHub, so please check it out too!
💾 GitHub Repository : Check the source code at this link
📌If you use Mesh and replace it with a 3D object, you can even get closer to your ideal!!
Meshy is a service that allows you to easily generate 3D objects using AI.
By using this, you can easily create your ideal 3D object, so I think you can get even closer to your ideal!
📺 Check Mesh : You can check it on the official page from this link

If you found this helpful, please subscribe to our channel!
We will continue to create lessons and works from TypeScript x React Three Fiber in the future!
We will be making an announcement on YouTube, so please subscribe to our YouTube channel and wait for notifications!
📺 Watch YouTube : You can watch it from this link
If you'd like to know what React Three Fiber can do, please refer to the following!
We have easy-to-use works available!
- I tried making bears walk with React x Three.js!
- I tried making an old man dance on React x Three.js!
- I tried to display a 3D model with React x Three.js!
- I made a 3D button that explodes in React x Three.js!
- React Three Fiber x Drei x Introduction to TypeScript! Poke Poke style 3D background made with standard objects!