assistant What you’re seeing is expected given one key issue: ambientMaterial(180) kills directional shading, so your lights have almost nothing to interact with. On top of that, p5.js lighting only produces flat diffuse shading unless you use a specular-capable material and normals are correct (they usually are for STL vertex lists, but only if geometry is clean). 🔧 Fix (minimal change, high impact) Replace your material + lighting block with this: ✅ Correct “CAD-style shaded preview” JavaScript // --- LIGHTING --- ambientLight(70); // main directional light (key for shading) directionalLight( 255, 255, 255, -0.6, 0.8, -1 ); // subtle fill light (prevents harsh black shadows) directionalLight( 80, 80, 80, 0.6, -0.3, 0.6 ); // --- MATERIAL --- noStroke(); // IMPORTANT: replace ambientMaterial specularMaterial(200); shininess(60); 🚨 Why yours is flat grey ❌ ambientMaterial(180) ignores lighting direction entirely removes highlights completely produces “clay grey” look So even perfect lights = no effect. 🔥 If it STILL looks flat, it’s usually one of these: 1. Normals missing / broken STL parsing If triangles are malformed (very common), lighting breaks visually. Check: JavaScript console.log(model.xs.length); Must be multiple of 9 vertices logically grouped. 2. Model is too small or too large Lighting falloff becomes invisible. Try scaling boost temporarily: JavaScript scale(2); inside fitModelToView. 3. Light direction is wrong relative to model Try this known-good lighting setup: JavaScript ambientLight(80); directionalLight(255,255,255, 1, -1, -0.5); directionalLight(120,120,120, -1, 1, 0.5); 🧠 Best “STL viewer preset” (use this) If you want something that immediately looks right: JavaScript ambientLight(90); directionalLight(255,255,255, -0.5, -1, -0.3); directionalLight(60,60,60, 0.5, 1, 0.3); noStroke(); specularMaterial(220); shininess(80); ⚡ One more important improvement (high impact) Move lighting OUT of the model block: Right now you do: JavaScript if (model) { ambientLight(...) directionalLight(...) That causes inconsistent accumulation across frames in some p5/WebGL setups. Better: JavaScript ambientLight(...) directionalLight(...) if (model) { draw mesh } If you want next step after this: I can show you how to add a ground plane + fake shadow projection, which is what really makes STL viewers feel “real”. --- user I still see no shade function draw3D() { orbitControl(); push(); if (model) { fitModelToView(150); // tweak this number (150–300 feels good) // --- LIGHTING (critical) --- ambientLight(70); // base fill so dark sides aren't black directionalLight( 255, 255, 255, // white light 0.6, 0.8, -1 // direction (top-right-front) ); // optional: adds a second softer fill light //directionalLight(80, 80, 80, 0.6, 0.3, 0.8); if (wireframe) { stroke(0); noFill(); } else { noStroke(); //ambientMaterial(180); //fill(180); //normalMaterial(); specularMaterial(200); shininess(60); } beginShape(TRIANGLES); for (let i = 0; i < model.xs.length; i += 3) { vertex(model.xs[i], model.ys[i], model.zs[i]); vertex(model.xs[i + 1], model.ys[i + 1], model.zs[i + 1]); vertex(model.xs[i + 2], model.ys[i + 2], model.zs[i + 2]); } endShape(); } pop(); } Show more --- assistant If you truly see zero shading difference at all, this is almost never a lighting problem anymore—it’s almost always one of these three structural issues in p5 WebGL: 🚨 1. Your geometry is likely not producing valid triangle faces You are feeding: JavaScript vertex(model.xs[i], model.ys[i], model.zs[i]); That can work, but only if: every 3 vertices form a valid triangle ordering is correct no NaNs exist no degenerate triangles (all points identical / collinear) 🔴 Common hidden bug You are assuming: “every 3 vertices = triangle” But STL files are usually: facet normal ... outer loop vertex ... vertex ... vertex ... endloop endfacet If parsing is even slightly off, you get garbage triangles → flat shading ✅ QUICK TEST (do this first) Replace your material + lighting with: JavaScript noStroke(); normalMaterial(); If this STILL looks flat: 👉 your geometry is broken, not lighting. 🧠 2. You may have flipped Y/Z but not normals consistency You do: JavaScript ys.push(z); zs.push(y); That changes winding order implicitly. If triangle winding is inconsistent → lighting breaks or collapses. 🔧 Fix: enforce consistent triangle winding Inside draw loop, try: JavaScript beginShape(TRIANGLES); normalMaterial(); If that suddenly works → problem is vertex order, not lights. ⚠️ 3. Your light direction is currently not effective This line: JavaScript directionalLight(255, 255, 255, 0.6, 0.8, -1); is not normalized, and p5 does NOT normalize it for you reliably. ✅ Fix light direction properly Use: JavaScript directionalLight(255, 255, 255, -0.6, -0.8, -1); or better: JavaScript directionalLight(255, 255, 255, -1, -1, -1); 🔥 4. The REAL debugging step (do this) Replace EVERYTHING in your draw mesh with: JavaScript noStroke(); normalMaterial(); beginShape(TRIANGLES); for (let i = 0; i < model.xs.length; i += 3) { vertex(model.xs[i], model.ys[i], model.zs[i]); vertex(model.xs[i + 1], model.ys[i + 1], model.zs[i + 1]); vertex(model.xs[i + 2], model.ys[i + 2], model.zs[i + 2]); } endShape(); Interpretation: ✅ If you now see color gradients: → geometry is fine → lighting issue ❌ If still flat grey: → geometry is broken OR all triangles degenerate 🧨 5. Most likely root cause (based on your earlier code) You previously had: JavaScript xs.push(x); ys.push(z); zs.push(y); That axis swap often breaks STL winding assumptions, meaning: triangles exist, but normals effectively cancel out visually 🧭 Best next step (high value fix) Instead of vertex arrays, you should store: JavaScript triangles = [ [[x,y,z],[x,y,z],[x,y,z]], ] Then render: JavaScript for (let t of triangles) { beginShape(TRIANGLES); vertex(...t[0]); vertex(...t[1]); vertex(...t[2]); endShape(); } This guarantees structural correctness. If you want, I can next: fix your STL parser properly (facet-aware, not vertex-stream) give you correct normals per face (huge visual upgrade) add real CAD-style shading (Blinn-Phong feel) or convert this into a proper lightweight slicer pipeline Right now you're very close—the issue is data structure, not rendering. --- user this is still flat (normal version is blue: function draw() { background(200); draw3D(); drawUI(); } function draw3D() { orbitControl(); push(); if (model) { fitModelToView(150); // tweak this number (150–300 feels good) // --- LIGHTING (critical) --- ambientLight(70); // base fill so dark sides aren't black directionalLight( 255, 255, 255, // white light 0.6, 0.8, -1 // direction (top-right-front) ); // optional: adds a second softer fill light //directionalLight(80, 80, 80, 0.6, 0.3, 0.8); if (wireframe) { stroke(0); noFill(); } else { noStroke(); //ambientMaterial(180); //fill(180); //normalMaterial(); //specularMaterial(200); shininess(60); } beginShape(TRIANGLES); normalMaterial(); for (let i = 0; i < model.xs.length; i += 3) { vertex(model.xs[i], model.ys[i], model.zs[i]); vertex(model.xs[i + 1], model.ys[i + 1], model.zs[i + 1]); vertex(model.xs[i + 2], model.ys[i + 2], model.zs[i + 2]); } endShape(); } pop(); } Show more --- assistant This narrows it down a lot. At this point, this is not a lighting/material issue at all. You are seeing flat shading because of this: ❗ You are using beginShape(TRIANGLES) but NOT supplying per-face normals. In p5.js WebGL: normalMaterial() only works if normals exist when using raw vertex() lists, p5 does not auto-generate correct face normals so lighting collapses → everything becomes uniformly shaded (flat look) 🔥 Root cause (definitive) You are doing: JavaScript beginShape(TRIANGLES); normalMaterial(); vertex(...) But: p5 expects normals per vertex you are not providing them so shading degenerates to flat / uniform result ✅ FIX (you need to explicitly compute normals) You must generate face normals per triangle. ✔️ Step 1: compute normals per triangle Replace your draw loop with this: JavaScript beginShape(TRIANGLES); for (let i = 0; i < model.xs.length; i += 3) { let ax = model.xs[i]; let ay = model.ys[i]; let az = model.zs[i]; let bx = model.xs[i + 1]; let by = model.ys[i + 1]; let bz = model.zs[i + 1]; let cx = model.xs[i + 2]; let cy = model.ys[i + 2]; let cz = model.zs[i + 2]; // edge vectors let ux = bx - ax; let uy = by - ay; let uz = bz - az; let vx = cx - ax; let vy = cy - ay; let vz = cz - az; // cross product (normal) let nx = uy * vz - uz * vy; let ny = uz * vx - ux * vz; let nz = ux * vy - uy * vx; // normalize let len = Math.sqrt(nx*nx + ny*ny + nz*nz) || 1; nx /= len; ny /= len; nz /= len; normal(nx, ny, nz); vertex(ax, ay, az); vertex(bx, by, bz); vertex(cx, cy, cz); } endShape(); 🧠 Why this fixes everything You are manually telling WebGL: “this triangle faces THIS direction” So now: directionalLight works specular highlights work shading gradients appear model stops looking like a flat silhouette ⚡ Alternative (easier but less correct) If you don’t want to compute normals: Replace everything with: JavaScript normalMaterial(); BUT: it will still look “flat rainbow shaded” no proper lighting response 🚨 Important insight (this is the real lesson) In WebGL (and p5): Method Requires normals? Result ambientMaterial ❌ flat normalMaterial ❌ debug color specularMaterial ✅ realistic lighting directionalLight ❌ does nothing alone You were doing everything except the one missing piece: normals If you want next upgrade I can help you: compute vertex normals (smooth shading instead of faceted) or convert STL → indexed mesh (massive performance + quality gain) or add real slicer-style shading (like PrusaSlicer / Cura preview) But first: get this face-normal fix in.