Practical LLM Development: “Carving” Michelangelo’s David Inside a Browser
This article presents a real-world experiment in using a large language model (LLM) to build a complete interactive 3D web experience—an interactive Chinese-language website that explains how Michelangelo’s David emerged from a block of marble. It details a prompt design strategy for creative tasks, a two-stage workflow (plan first, code second), and a production-grade implementation that uses GLSL fragment shaders to simulate subtractive carving, a scroll-driven narrative timeline, and mobile-friendly rendering with zero external assets. The piece also offers honest observations about LLM strengths and weaknesses in creative engineering, and distills a repeatable methodology for ambitious model-assisted projects.
Practical LLM Development: “Carving” Michelangelo’s David Inside a Browser
1. Why This Experiment: A “Creative Task” as a True Test of LLM Limits
In everyday development, most tasks we hand to large language models have clear boundaries: fix a bug, complete a function, migrate code from one paradigm to another. These tasks have well-defined acceptance criteria, yet they fail to answer a more fundamental question—when the task provides only a goal and no prescribed path, can a model behave like a true creator, completing the full journey from concept to finished work?
So I set myself a challenge: build an interactive Chinese-language website that helps a general audience understand how Michelangelo’s David was born from a raw block of stone. My hope was not that viewers would memorize a few historical facts, but that they would gain a new understanding of “the act of carving” itself—and be willing to pause and observe the work for a while. The narrative angle, visual style, interaction model, and technical approach were all left open for the model to decide.
Choosing David was no accident. Michelangelo’s famous remark—“The sculpture is already complete within the marble block, I just chisel away the superfluous material”—maps naturally onto a 3D rendering paradigm of subtractive geometry, where a complete form is progressively revealed. This challenge tests four dimensions of the model’s capability:
- Domain knowledge: Are the marble’s origins, the historical timeline, and the carving process accurate?
- Frontend engineering: Can it deliver a single-file page that is well-structured and maintainable?
- Graphics fundamentals: Can it implement a shader-based transition “from stone to figure”?
- Narrative design: Can it structure the content into a rhythmic, emotionally engaging experience rather than a stack of facts?
The first two are routine; the last two are the real differentiators. This article is a full retrospective on the technical approach and the collaborative methodology used.
2. Translating “Ambition” into an Engineering Problem
Before writing the prompt, I did a round of requirement abstraction myself. No matter how grand the creative intent, it can be decomposed into four layers at the engineering level:
- Content layer: Historical facts and a narrative script. This block of marble had been started and abandoned by two sculptors before the 16th century, left untouched for decades, and deemed unworkable by everyone. A 26-year-old Michelangelo took on this “death-sentenced” stone and completed a masterpiece over five meters tall in about three years. These materials had to be organized into chapters, not a timeline of bullet points.
- 3D layer: The visual centerpiece. There is only one core metaphor—subtraction. The page should not show a statue “growing”; it should show stone “being removed.”
- Interaction layer: How the audience participates. Scroll-as-timeline is the most intuitive approach and the one that best carries a sense of process.
- Performance layer: Constraints. A single-file deliverable, zero external resources, and smooth mobile rendering—this is both an engineering discipline and a forcing function that pushes the solution toward procedural generation rather than asset stacking.
There is one more constraint that is easy to overlook: honesty. Without real scan data, the “David” on the page could only be a stylized, procedurally generated approximation. The plan required a clear footer note stating “schematic illustration, not a scanned replica,” to avoid misleading viewers. This later proved essential—viewers are far more tolerant of “not looking exactly like the original” than of “pretending to be the real thing.”
3. Prompt Design: A Communication Pattern for Creative Tasks
Creative tasks require a fundamentally different prompt style than functional tasks. Functional prompts need clear input/output boundaries; creative prompts need to communicate goal, audience, and emotional intent—then leave the implementation path open. The prompt skeleton I ultimately used was:
Please complete an ambitious creative work:Build an interactive Chinese-language website that helps a general audience understandhow Michelangelo's David was born from a block of stone.I want the audience to walk away not just with a few historical facts,but with a new understanding of "the act of carving" itself,and to be willing to pause and observe this work.Specific constraints:1. The narrative angle, visual style, interaction model, and technical approach are up to you;2. Single-file delivery, no external art assets or network requests;3. Must run smoothly on mobile browsers;4. The statue on the page is a stylized representation and must be clearly labeled as such.
Here is the intent behind each line:
- “Ambitious creative work”: Raises the model’s target anchor, pushing it to plan as a piece of work rather than a web page;
- Emotional goal (“willing to pause and observe”): This is the most critical sentence in the entire prompt—it shifts the acceptance criteria from information completeness to experience quality, affecting camera pacing and whitespace design directly;
- Deliberate openness (“technical approach is up to you”): This seems like delegation, but it is actually a quality strategy—letting the model propose a solution before writing code yields far better output than locking the implementation path upfront;
- Engineering constraints upfront: Avoids a “beautiful but unusable draft” that depends on heavy assets or a desktop-only environment.
Another key practice is the two-stage workflow: first ask the model to produce a creative plan—covering narrative storyboarding, visual specifications (color palette, material, typography hierarchy), interaction design, and technical selection—then, after human review, ask it to implement the code based on the plan. This mirrors human team review processes and minimizes rework.
4. The 3D Layer: Subtractive Carving with a Fragment Shader
4.1 Core Idea
The rendering scene contains two layers of geometry: an inner layer is the stylized human mesh; an outer layer is a “stone shell” created by offsetting outward from the figure’s silhouette by a small thickness. The narrative is carried by the shell’s fragment shader—it discards fragments progressively based on a global carving progress uProgress (from 0 to 1), simulating the “chiseling away” process. The deeper the progress, the more shell is removed, and the figure gradually emerges from the stone.
4.2 Shader Pseudocode
uniform float uProgress; // Global carving progress 0.0 → 1.0varying vec3 vPos; // Model-space coordinates// Carving threshold: carving starts from the front and near the axis,// then spreads outward,// with fractal noise to simulate random chisel marksfloat carveThreshold(vec3 p) {float front = (p.z + 1.0) * 0.30; // Front is carved earlierfloat radial = length(p.xy) * 0.35; // Further from axis = laterfloat noise = fbm(p * 3.7); // Chisel mark randomnessreturn clamp(front + radial + noise * 0.35, 0.0, 1.0);}void main() {// Regions where threshold is below current progress = already carved → discardif (carveThreshold(vPos) < uProgress) discard;// Remaining regions are shaded as marble: procedural texture, zero texture costvec3 marble = mix(vec3(0.94), vec3(0.83), fbm(vPos * 6.0));gl_FragColor = vec4(marble, 1.0);}
Two details pushed the effect beyond a “toy” feel:
- Surface roughness varies with progress: Early chapters use high-frequency noise to perturb normals, giving rough chisel marks; the final chapter attenuates the perturbation to simulate polishing. The entire transition is blended on the same noise data, requiring no switching animation.
- Debris particles at carving moments: At chapter transitions, a small burst of short-lived particles is emitted, reinforcing the physical intuition of “chiseling.” The particle count is strictly limited to avoid frame drops on mobile.
5. Interaction and Narrative: Scroll as Timeline
5.1 Chapter Structure
The narrative is organized into six chapters, each bound to a camera position and a carving progress value:
const chapters = [{ at: 0.00, title: "A Block of Marble Abandoned for Decades", progress: 0.00, cam: { az: -2.2, el: 0.35, dist: 7.2 } },{ at: 0.18, title: "Reading the Stone: Letting the Giant Stand", progress: 0.10, cam: { az: -1.4, el: 0.22, dist: 6.0 } },{ at: 0.42, title: "Roughing Out: Bold Subtraction", progress: 0.45, cam: { az: 0.3, el: 0.10, dist: 5.2 } },{ at: 0.68, title: "Details: From Ankles to Curls", progress: 0.80, cam: { az: 1.1, el: 0.55, dist: 3.0 } },{ at: 0.88, title: "Shaping: Polishing and Completion", progress: 1.00, cam: { az: 1.9, el: 0.30, dist: 4.4 } },{ at: 1.00, title: "Epilogue: Observe It Yourself", progress: 1.00, cam: { az: 2.8, el: 0.25, dist: 6.8 } },];function sampleTimeline(t) {let i = 0;while (i < chapters.length - 2 && chapters[i + 1].at < t) i++;const a = chapters[i], b = chapters[i + 1];const k = smoothstep(a.at, b.at, t); // Smooth easing to avoid camera jitterreturn { progress: lerp(a.progress, b.progress, k), cam: lerpCam(a.cam, b.cam, k) };}
5.2 Making “Willing to Pause” Real
“Pausing to observe” is a design goal, not a slogan. It is delivered by three concrete details: the epilogue chapter removes all UI elements and hands full camera control to the viewer’s drag; each chapter’s text is limited to one quote and two lines of facts, keeping density deliberately low; camera motion uses smooth easing throughout—wide shots for the roughing-out chapter, close-ups for the detail chapter—replacing verbal emphasis with changes in shot scale.
Accessibility is equally non-negotiable: text is rendered in the DOM rather than drawn on canvas, ensuring it is searchable and screen-reader friendly; when the system’s “reduce motion” preference is detected, chapter buttons provide direct jumps instead of continuous scroll animation.
6. Performance and Deployment
- Zero assets, minimal payload: No textures, no external models; geometry is procedurally generated, and the initial page weight is measured in tens of kilobytes;
- Batched draws: The figure and the stone shell each use one draw call, particles are batched, and the total draw call count is in the single digits;
- Mobile guardrails: The device pixel ratio is clamped to 2, scroll events are throttled, and the rendering loop pauses when the page is not visible;
- Deployment as static hosting: This zero-dependency artifact has minimal launch overhead. Upload the file to an object storage service, attach a CDN, and it is ready for distribution. Mainstream cloud platforms—such as Baidu AI Cloud—offer this kind of general-purpose capability, with no server-side logic required.
7. Real-World Collaboration with an LLM: Observations and Engineering Advice
I will be honest: the process was not “one prompt in, finished product out.” The model’s performance was clearly layered:
- Strengths: Solution planning, chapter copy, first drafts of shader math, and overall architecture—high-quality output on the first pass;
- Weaknesses: “Feel-based” parameters—spatial proportions (shell thickness, the relative distance between the statue and the camera), animation timing, and noise parameters—required multiple iterations.
Three iterative methods proved effective: plan before code; change one variable per round so feedback maps cleanly to modifications; and manually verify critical math, especially coordinate transforms and threshold ranges inside shaders—ask the model to explain the formula’s intent, then check edge cases line by line. In the end, I codified the prompt template and an acceptance checklist (runnable, smooth on mobile, complete narrative, frame rate target met, honest labeling), which can be reused directly for subsequent projects.
8. Conclusion: Carving Is Subtraction
The biggest takeaway from this exercise was not the page itself but a repeatedly validated insight: the core of interactive narrative is not technical spectacle—it is translating a domain-specific way of thinking—carving is subtraction—into an operable experience. The same principle applies to prompt writing: rather than listing features, convey the way of thinking.
Michelangelo said the statue was already inside the stone. So is good code: refactoring is chiseling away redundancy, and iterating with a model is also chiseling away wrong versions until the form that “was always meant to be there” remains. If you too have an ambitious creative challenge sitting on the shelf, try this workflow once—tools will keep changing, but the methodology of “first clarifying what you want people to understand” will always come first.
