<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Three.js |</title><link>https://changshenghsun.com/tags/three.js/</link><atom:link href="https://changshenghsun.com/tags/three.js/index.xml" rel="self" type="application/rss+xml"/><description>Three.js</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Fri, 08 May 2026 00:00:00 +0000</lastBuildDate><image><url>https://changshenghsun.com/media/icon_hu_d41247041d33f7ba.png</url><title>Three.js</title><link>https://changshenghsun.com/tags/three.js/</link></image><item><title>MakeNTU</title><link>https://changshenghsun.com/projects/makentu/</link><pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate><guid>https://changshenghsun.com/projects/makentu/</guid><description>&lt;p&gt;&lt;strong&gt;May 8–10, 2026&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="the-brief"&gt;The brief&lt;/h2&gt;
&lt;p&gt;AUO makes display panels, and the sponsor prompt came down to a single
question: if the windshield itself becomes the display, what is actually worth
putting on it? The easy answer is a bigger speedometer. We treated it as an
interface problem instead — the glass should show what the driver&amp;rsquo;s current
state calls for, and stay out of the way otherwise.&lt;/p&gt;
&lt;h2 id="what-we-built"&gt;What we built&lt;/h2&gt;
&lt;p&gt;A first-person driving simulator that runs entirely in the browser, built on
real map data: the Xinyi district road network pulled live from OpenStreetMap
Overpass, routes from OSRM, and procedurally generated buildings filling the
blocks in between. Four scenarios ship — city, highway, coastal drive, and a
parked rest stop.&lt;/p&gt;
&lt;p&gt;The windshield is composed in four layers, so the result reads as glass rather
than as a game overlay:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What is in it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4 · Glass&lt;/td&gt;
&lt;td&gt;Vignette, film grain, top tint, A-pillars and mirror silhouettes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 · HUD&lt;/td&gt;
&lt;td&gt;Frosted DOM/SVG panels — speed, compass, nav card, ETA, climate, now playing, minimap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 · AR&lt;/td&gt;
&lt;td&gt;POI cards projected into screen space onto building facades, fading when occluded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 · World&lt;/td&gt;
&lt;td&gt;Three.js — roads, buildings, cabin, traffic agents, route arrows, destination pillar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And the subsystems that make it more than a renderer:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subsystem&lt;/th&gt;
&lt;th&gt;How it works&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Driver monitoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MediaPipe FaceLandmarker blendshapes feed one state machine with two outputs. Hard warning: both eyes shut past 1.2 s, cooldown-gated so it does not machine-gun at a driver who is genuinely asleep. Continuous 0–1 fatigue score: rolling PERCLOS (a one-minute EMA of eyelid closure) combined with yawn rate — a jaw held open past 0.55 for 800 ms counts as a yawn, over a two-minute window.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fatigue-driven HUD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The score is the interesting half. As fatigue climbs the HUD raises contrast on rear traffic, sheds non-essential chrome, and drifts wake-up particles across the glass — the display responds to the driver instead of shouting at them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Head-pose lane-change intent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Looking more than 18° to one side for 800 ms reads as a shoulder check, and the panel surfaces rear traffic in that adjacent lane. Releases at a looser 8° with a 500 ms cooldown — hysteresis, so a brief glance forward does not flicker the panel on and off.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Door-opening safety gate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In rest mode the car is parked roadside with traffic flowing past. Asking to get out forward-simulates the traffic schedule: if any vehicle will come within 12 m of the door in the next 4 s the gate stays red, otherwise a tap confirms.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice + AI reservation call&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speech recognition is always listening. Say 「找餐廳」and five nearby POIs come back from Overpass; pick one by number and the in-car assistant dials it, speaking a reservation script over TTS while the overlay renders the transcript as chat bubbles. Phone numbers carry a separate spoken form so Chinese TTS reads them digit by digit instead of turning 09 into a place value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Four-screen cabin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each screen runs the same app with &lt;code&gt;?role=driver / passenger / left / right&lt;/code&gt;. The driver publishes state snapshots to a small WebSocket relay that fans them out unparsed; each role applies its own camera offset — passenger 0.45 m inboard of the right door, side windows yawed ±90° — and keeps its own mouse-look on top, so a passenger can still turn their head independently.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Physical steering wheel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You drive it with an actual wheel. Steering angle comes off a six-axis IMU (three-axis accelerometer plus gyroscope) mounted on the wheel itself, so the rotation is read directly from how the wheel moves — no shaft encoder or mechanical end-stops to fabricate in three days. An Arduino Nano packages the reading as one JSON line per packet at 115200 baud, roughly 20 packets a second, which the browser reads through Web Serial. Every value is clamped on arrival so a noisy reading cannot push the vehicle physics past its own caps, and the 60 fps render interpolates between packets rather than stepping.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;
&lt;figure id="figure-demoing-to-the-judges--hands-on-the-laser-cut-control-rig-the-simulator-running-on-the-screen-in-front-of-me"&gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="Demoing the simulator to the judges with hands on the laser-cut control rig"
srcset="https://changshenghsun.com/projects/makentu/judging-demo_hu_ba6de5e9835c4b8.webp 320w, https://changshenghsun.com/projects/makentu/judging-demo_hu_769bc8dca1b8db81.webp 480w, https://changshenghsun.com/projects/makentu/judging-demo_hu_cbdeeb749b3a6359.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://changshenghsun.com/projects/makentu/judging-demo_hu_ba6de5e9835c4b8.webp"
width="760"
height="507"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Demoing to the judges — hands on the laser-cut control rig, the simulator running on the screen in front of me.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="my-role"&gt;My role&lt;/h2&gt;
&lt;p&gt;I was the team lead. The sponsor prompt named a display, not a product, so most
of the work up front was deciding what a windshield should actually do — I ran
that ideation with the team, and then led the software side through to a working
demo. My own commits landed the highway, coastal and rest-stop scenarios, the
driver-monitoring pipeline and everything the fatigue score drives, collision
handling, the head-pose lane-change intent, and the exit gate.&lt;/p&gt;
&lt;p&gt;Roughly 20k lines of TypeScript went in over the three days, with
194 unit tests across 16 files covering the pure logic — the state machines,
geometry and traffic simulation are all written as pure functions precisely so
they could be tested at hackathon speed instead of debugged through the UI.&lt;/p&gt;
&lt;p&gt;
&lt;figure id="figure-team-37-nothingunique--劉允中-賴泓安-鄭竣陽-胡軒綸-張勝勛--at-our-bench-on-the-auo-track"&gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="The five-person team at our bench during the hackathon"
srcset="https://changshenghsun.com/projects/makentu/team-bench_hu_c3d440bc2ec8763f.webp 320w, https://changshenghsun.com/projects/makentu/team-bench_hu_c0affe0bea0380a6.webp 480w, https://changshenghsun.com/projects/makentu/team-bench_hu_1461225f7144b0f0.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://changshenghsun.com/projects/makentu/team-bench_hu_c3d440bc2ec8763f.webp"
width="760"
height="507"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Team 37, NoThingUnique — 劉允中, 賴泓安, 鄭竣陽, 胡軒綸, 張勝勛 — at our bench on the AUO track.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="results"&gt;Results&lt;/h2&gt;
&lt;p&gt;First place, AUO corporate award.&lt;/p&gt;</description></item></channel></rss>