🔧 Programmierung 🕛 vor 1 Monat 5 Min Lesezeit
0

Building a Three.js 3D Product Configurator for WooCommerce: 4 Things I Didn't Expect

↗ Quelle (dev.to)
🗣️ Stimme:

Most WooCommerce product pages still show the same thing stores have shown for 20 years: a handful of flat photos. I spent the last few months building Noorifa, a plugin that replaces that with an interactive Three.js viewer — customers rotate the model, zoom in, and switch colors/materials on specific meshes in real time, synced to the store's actual WooCommerce variations.



The 3D rendering part was the easy 20%. The other 80% was a series of small, specific problems that don't show up in a Three.js tutorial. Here are four of them.






1. A directional light rig can't light a face it can't see



Early on, customers rotating a table model would find the underside of the tabletop rendering near-black — no matter how far I pushed the light intensity.



The rig at the time was a single key light plus a hemisphere ambient:




CODE
scene.add( new THREE.HemisphereLight( 0xffffff, 0x444444, 1.2 ) );

const keyLight = new THREE.DirectionalLight( 0xffffff, 1.2 );
keyLight.position.set( 3, 5, 4 );
scene.add( keyLight );






The bug was geometric, not a brightness problem: keyLight sits above the model, so its light direction only reaches surfaces whose normal faces back toward it. A downward-facing surface — the underside of an overhanging tabletop — can't receive any direct contribution from a light positioned above it, at any intensity. Cranking the brightness slider was scaling a number that was multiplying against zero.



The fix was closer to actual three-point studio lighting: key, fill, and rim from above for shape and separation, plus a dedicated light from below, and a brighter hemisphere ground color to approximate bounced light:



scene.add( new THREE.HemisphereLight( 0xffffff, 0x888888, 1.1 * brightness ) );



const keyLight = new THREE.DirectionalLight( 0xffffff, 1.1 * brightness );

keyLight.position.set( 3, 5, 4 );



const fillLight = new THREE.DirectionalLight( 0xffffff, 0.5 * brightness );

fillLight.position.set( -4, 2, 3 );



const rimLight = new THREE.DirectionalLight( 0xffffff, 0.45 * brightness );

rimLight.position.set( -1, 3, -5 );



const underLight = new THREE.DirectionalLight( 0xffffff, 0.55 * brightness );

underLight.position.set( 0, -5, 2 );



None of these cast shadows, so the GPU cost is negligible — it's just four more cheap terms in the lighting sum, not a new render pass.




  1. Unbounded OrbitControls zoom looks like a bug, not a feature
    By default, OrbitControls' zoom range is [0, Infinity]. Customers were scrolling the model either through the near clipping plane or out until it was a speck — both read as "this is broken," not "I zoomed."



The fix is bounding minDistance/maxDistance, but the useful bit is what to scale them against. A fixed number breaks the moment two products have wildly different physical sizes — a ring and a wardrobe can't share a zoom range. Scaling off the model's own auto-computed framing radius fixes that:



`function applyFraming( framing ) {

camera.near = framing.radius / 100;

camera.far = framing.radius * 100;

camera.updateProjectionMatrix();

controls.target.copy( framing.center );




CODE
// Initial camera distance is framing.radius * sqrt(3) ≈ 1.73,
// so [1, 4] always brackets it comfortably regardless of the
// product's actual size.
controls.minDistance = framing.radius;
controls.maxDistance = framing.radius * 4;




}`



Every product gets a zoom range proportional to itself, computed at load time from its bounding box, instead of a number that only happens to work for whatever model I was testing with.




  1. Draco, and the WASM question (corrected from an earlier post of mine)
    I originally claimed here — and in a Reddit post — that "WordPress.org disallows bundling .wasm binaries." A commenter fairly asked me to point to that rule, and I couldn't find one. Correcting it properly:



There's no line item that names WebAssembly specifically. What actually exists is broader: WordPress.org's plugin guidelines require all distributed code to be non-obfuscated and available as human-readable source. A compiled .wasm binary doesn't qualify as source in any form, so in practice it runs into that same rule — but that's an inference on my part, not a cited policy. Worth being precise about the difference.



Practical effect is the same either way: Draco's decoder ships in both a WASM build (faster) and a pure-JS build (slower, fully inspectable as source). For WP.org distribution, I force the JS one:



dracoLoader.setDecoderConfig( { type: 'js' } );

Noticeably slower to decode on large meshes than the WASM path would be — the real cost of going through a plugin directory with source-transparency requirements, not a rule I can point to by name.




  1. Don't reinvent variation pricing — hand off to WooCommerce's own form
    The tempting-but-wrong approach when a customer clicks a color swatch: fetch the variation's price yourself and update the DOM. Don't — WooCommerce already has a variation form with its own price/stock lookup, AJAX-cached, that themes and other plugins already hook into. Reimplementing it means every theme customization and every other plugin's price-affecting hook silently stops working for your swatches.



Instead, the swatch click just drives WooCommerce's own native :

button.addEventListener( 'click', () => {
select.value = swatch.selectValue;
window.jQuery( select ).trigger( 'change' );
currentSelections[ group.attributeKey ] = swatch.value;
recomputeAndApply(); // repaints the 3D model's material
syncActive();
} );

recomputeAndApply() handles the visual side (swapping the mesh's material). Everything price/stock-related comes from WooCommerce itself reacting to that change event, the same as if the customer had used the dropdown directly. One nice side effect: if a store's variation data has an inconsistency (a value left on "Any" instead of a specific option, which silently breaks WooCommerce's own price matching), it breaks the same way it would with the native dropdown — which made it easy to build a diagnostic warning for, instead of a mystery bug report.

Curious if anyone here has hit the WASM-via-obfuscation-rule issue on other plugin directories, or has a cleaner pattern than event-triggering a native for handing control back to a host platform's own logic.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
4 Quellen
CVE-2026-9176 | IBM WebSphere Application Server 8.5/9.0 improper authentication (WID-SEC-2026-3255)
2 Quellen
CVE-2026-86815 | BackWPup Plugin up to 5.7.4 on WordPress REST API Routes authorization (EUVD-2026-75973)
1 Quelle
CVE-2026-19486 | Google Cloud Gemini Enterprise Agent Platform App Builder prior 2026-06-01 server-side request forgery (EUVD-2026-76021)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building a Three.js 3D Product Configurator for WooCommerce: 4 Things I Didn't Expect

Thematisch verwandte Begriffe: Building, Threejs, Product, Configurator · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...