惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
I
InfoQ
The Register - Security
The Register - Security
L
LangChain Blog
H
Help Net Security
The GitHub Blog
The GitHub Blog
S
Schneier on Security
博客园 - 【当耐特】
W
WeLiveSecurity
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
The Cloudflare Blog
H
Heimdal Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
雷峰网
雷峰网
N
Netflix TechBlog - Medium
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Visual Studio Blog
博客园 - 聂微东
PCI Perspectives
PCI Perspectives
Last Week in AI
Last Week in AI
A
Arctic Wolf
宝玉的分享
宝玉的分享
T
The Blog of Author Tim Ferriss
S
Secure Thoughts
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
SegmentFault 最新的问题
SecWiki News
SecWiki News
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Schneier on Security
Schneier on Security
P
Proofpoint News Feed
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AI
AI
Engineering at Meta
Engineering at Meta

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
I Built an AI 3D Model Generator — Here's How I Handle Meshes in the Browser
Dyle Nazarro · 2026-05-25 · via DEV Community

I shipped www.imgto3d.ai a few months ago. It's an AI-powered 3D model generator: you upload an image, pick a generator (upscale, denoise, face recovery, or full restoration), and get back a .glb file you can drop into Blender, Unity, or straight into a web page.

The AI backend part — calling Replicate, polling for results, handling credits — was straightforward. The part that actually made me lose sleep? Getting those generated 3D assets to render smoothly inside a Next.js app without turning the user's phone into a hand warmer.

This is the story of how I built the frontend, and the specific performance tricks I had to pull to make browser-based 3D preview not feel like a PowerPoint from 2003.


What the Product Actually Does

ai3dgen.com runs four generators:

  • Upscale — takes a low-poly preview and bumps the geometry density.
  • Denoise / Unblur — cleans up noisy AI outputs.
  • Face Recovery — fixes distorted facial topology on character models.
  • Restoration — full pipeline for damaged or low-quality inputs.

Users pick their settings, burn some credits, and get an email when the mesh is ready. The critical moment is when they click "Preview" and expect to spin the model around in 3D right there in the browser.

That preview is where everything almost fell apart.


The Problem: AI Doesn't Care About Your Frame Rate

When users want to turn a 2D image into a 3D model online, they expect two things: fast processing and a crisp, interactive preview. The AI pipeline delivers beautiful meshes — thousands of polygons, 4K textures, the works. But it delivers them with zero optimization. There's no LOD. No texture atlasing. No one ran it through a decimation modifier.

My first attempt was embarrassingly naive: dump the raw .glb into a <Canvas> and call it a day. On my M2 MacBook, buttery smooth. On a 2021 Android phone? Tab crash. iOS Safari? "A problem occurred with this webpage so it was reloaded." Every. Single. Time.

I realized I wasn't just building a product — I was building a 3D runtime that had to survive the wild west of consumer hardware.


The Stack

  • Next.js 14 (App Router) — SSR for SEO, client components for the viewer.
  • Tailwind CSS — because I'm not writing raw CSS in 2026.
  • React Three Fiber (R3F) + @react-three/drei — declarative 3D scenes as React components. If you're still wrapping raw Three.js in useEffect, you're making life harder than it needs to be.
  • Replicate API — the AI pipeline that generates the actual meshes.

R3F makes it easy to get something on screen. It does not make it easy to get something performant on screen. That part is on you.


The Viewer Component (Production Code)

Here's the actual component running in production. Every line in here exists because something broke in production first.

import React, { Suspense, useMemo } from 'react';
import { Canvas } from '@react-three/fiber';
import { OrbitControls, useGLTF, Center, Environment } from '@react-three/drei';

function Model({ url }: { url: string }) {
  // useGLTF caches by URL. But if your URL is a signed S3 link 
  // that changes every refresh, congrats — you just disabled caching.
  const { scene } = useGLTF(url, true);

  // Clone the scene so multiple instances don't share materials
  const clonedScene = useMemo(() => scene.clone(), [scene]);

  return (
    <primitive 
      object={clonedScene} 
      scale={1.5} 
      castShadow 
      receiveShadow 
    />
  );
}

function Loader() {
  return (
    <div className="absolute inset-0 flex flex-col items-center justify-center bg-[#0d0e12] text-white z-10">
      <div className="h-10 w-10 border-2 border-gray-700 border-t-cyan-500 rounded-full animate-spin" />
      <p className="mt-4 text-sm text-gray-400">Decoding mesh...</p>
    </div>
  );
}

export default function Viewer3D({ modelUrl }: { modelUrl: string }) {
  return (
    <div className="relative w-full h-[500px] bg-[#0d0e12] rounded-xl overflow-hidden border border-gray-800">
      <Suspense fallback={<Loader />}>
        <Canvas
          camera={{ position: [0, 1.5, 4], fov: 45 }}
          gl={{ 
            antialias: true, 
            preserveDrawingBuffer: true,
            powerPreference: "high-performance"
          }}
          dpr={[1, 2]} // Clamp pixel ratio — retina screens will murder your FPS otherwise
        >
          <ambientLight intensity={0.6} />
          <directionalLight 
            position={[5, 5, 5]} 
            intensity={1.0} 
            castShadow 
            shadow-mapSize-width={1024}
            shadow-mapSize-height={1024}
          />

          <Center>
            <Model url={modelUrl} />
          </Center>

          <OrbitControls 
            enablePan={false}
            enableZoom={true}
            minDistance={1.5}
            maxDistance={8}
            maxPolarAngle={Math.PI / 1.5} // Prevent going below the ground
            autoRotate
            autoRotateSpeed={1.0}
          />

          <Environment preset="city" />
        </Canvas>
      </Suspense>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode


Three Things I Learned the Hard Way

1. Draco Compression Is Non-Negotiable

The AI backend originally served raw GLBs. A typical output was 45MB. On a 3G connection, that's 30+ seconds of staring at a spinner. Users bounce before the first vertex loads.

I added server-side Draco compression. File sizes dropped to ~8MB. Visually identical. The catch? You need to tell useGLTF to use a Draco decoder, and that decoder needs a .wasm file. Host that .wasm on a different domain without CORS headers? Silent failure. The model just never loads, and you get no error in Sentry.

// In your app entry or a useEffect
useGLTF.setDRACOLoader(new DRACOLoader().setDecoderPath('/draco/'));

Enter fullscreen mode Exit fullscreen mode

Also, Draco decoding is CPU-intensive. I throttle it by limiting concurrent loads — if a user rapidly switches between models in the gallery, I cancel the previous request. Otherwise, the main thread chokes and the UI freezes.

2. iOS Safari Is the Final Boss

Apple's WebGL implementation on iOS has a hard memory limit. Not a suggestion — a hard ceiling. Exceed it, and Safari nukes your tab without warning. No exception. No graceful degradation. Just reload.

I discovered this testing a high-poly model with an 8K texture. Chrome desktop? Perfect 60 FPS. iPhone 14? Instant reload.

My fixes:

  • Cap texture resolution at 2K for mobile (detected via navigator.hardwareConcurrency + touch event sniffing).
  • Clamp dpr to [1, 1.5] on mobile instead of [1, 2].
  • Implemented a "Lite Mode" toggle that swaps to a lower-poly version of the mesh. I hate that it exists, but users on older devices love it.

3. Don't Trust the Default Lighting

A 3D model under default lighting looks like a plastic toy from a gas station. I spent way too long tweaking individual lights before realizing R3F's <Environment> component with a preset solves 90% of the problem. It gives you realistic reflections and ambient occlusion for free.

The other 10%? A subtle contact shadow under the model using <ContactShadows> from Drei. It grounds the object and makes it feel like it's sitting on a surface, not floating in a void.


Why This Architecture?

I chose Next.js + R3F because I needed two things that usually fight each other:

  1. SEO — the marketing pages need to rank. Next.js App Router handles that.
  2. Interactive 3D — the viewer needs to be a rich client experience. R3F handles that.

The split is clean: server components for landing pages, metadata, and auth. Client components wrapped in 'use client' only where the Canvas lives. I keep the bundle small by dynamically importing the viewer:

const Viewer3D = dynamic(() => import('@/components/Viewer3D'), { ssr: false });

Enter fullscreen mode Exit fullscreen mode

This way, the Three.js payload only hits users who actually click "Preview." Everyone else gets a fast, lightweight landing page.


Where It Stands Now

ai3dgen.com is live. It handles four distinct generation pipelines, serves 12 languages, and processes thousands of conversions per week. The 3D viewer went from "crash my phone" to "smooth on a 4-year-old mid-range Android."

The hardest part wasn't the AI. It was respecting the user's hardware.


Try It / Break It

If you're building something with in-browser 3D — an AI tool, a product configurator, a portfolio — the ecosystem is mature enough that you don't need a graphics PhD. R3F + Drei + a bit of performance paranoia gets you 90% of the way there.

If you want to stress-test your own 3D viewer, image-to-3d AI will give you some gloriously unoptimized GLBs to throw at it. Upload an image, grab the mesh, and see if your renderer survives.

How are you handling 3D assets in your web apps? Running into the same memory limits, or have you jumped to WebGPU already? Drop a comment — always curious to see how others are solving this.