← Flight Simulator results

Flight Simulator

GPT 6.1 Sol · full

Initial view of Flight Simulator

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 38,285 bytes · SHA-256 44e21b1acb03
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Apex Aero | Flight operations</title><style>
@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');
:root{--lime:#d5f66c;--muted:#88969a;--panel:#111c20;--line:#ffffff16}*{box-sizing:border-box}body{margin:0;background:#091115;color:#edf2f1;font:13px Inter,Arial,sans-serif;overflow:hidden}button{font:inherit;cursor:pointer;color:inherit}button:focus-visible{outline:2px solid var(--lime);outline-offset:3px}header{height:76px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 32px;background:#0c1519}.brand{display:flex;gap:13px;align-items:center;font-size:19px;font-weight:700;letter-spacing:2px}.brand svg{width:34px;height:34px}.brand small{display:block;color:#879499;font-size:9px;letter-spacing:3px;margin-top:5px}nav{display:flex;gap:31px;height:100%;align-items:center}nav span{color:#6a797f;font-size:12px}nav .active{color:var(--lime);height:100%;display:flex;align-items:center;border-bottom:2px solid var(--lime)}.sys{font-size:10px;letter-spacing:1.5px;color:#a6b5b7}.dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--lime);margin-right:9px;box-shadow:0 0 12px #d5f66c44}.layout{display:grid;grid-template-columns:318px 1fr;height:calc(100vh - 76px)}aside{border-right:1px solid var(--line);padding:27px 23px;overflow-y:auto;background:#0e171b} .eyebrow{font-size:9px;letter-spacing:2px;color:#839398;text-transform:uppercase}.aside-heading{margin:9px 0 23px;font-size:23px;letter-spacing:-.6px}.section-label{display:flex;justify-content:space-between;font-size:10px;letter-spacing:1.5px;color:#a1afb2;margin:22px 0 12px}.section-label span{color:#53676d}.plane-card{background:#131f24;border:1px solid #26353a;margin:9px 0;border-radius:5px;padding:14px;cursor:pointer;position:relative;transition:.2s}.plane-card:hover{background:#1b292e}.plane-card.selected{border-color:var(--lime);background:linear-gradient(120deg,#242f24,#152125)}.plane-card .row{display:flex;justify-content:space-between;align-items:center}.plane-card h3{font-size:14px;margin:0 0 5px}.plane-card p{font-size:10px;color:#8b9ca1;margin:0}.plane-card .check{font-size:12px;color:var(--lime)}.plane-sil{height:51px;width:100%;margin:10px 0 5px;opacity:.8}.specs{display:flex;gap:20px;font-size:10px;color:#86979b}.specs b{color:#c5d0d1;font-weight:500;margin-right:3px}.mode{width:100%;text-align:left;padding:13px;background:transparent;border:1px solid #29373d;border-radius:5px;margin-bottom:8px;display:flex;gap:12px;align-items:center}.mode.selected{border-color:#82994c;background:#d5f66c09}.mode i{font-style:normal;font-size:19px;color:#a5b5b8}.mode strong{font-size:12px;display:block;margin-bottom:4px}.mode small{font-size:10px;color:#83969c}.launch{width:100%;border:0;background:var(--lime);color:#1b2715;border-radius:4px;margin-top:17px;padding:15px;font-size:12px;font-weight:700;letter-spacing:.6px;display:flex;justify-content:space-between}.launch:hover{background:#e2ff91}.side-foot{color:#6b7f85;font-size:9px;line-height:1.8;margin-top:17px;display:flex;justify-content:space-between}.main{padding:27px 28px 18px;min-width:0;display:flex;flex-direction:column;gap:18px}.topline{display:flex;align-items:center;justify-content:space-between}.topline h1{font-size:24px;font-weight:500;letter-spacing:-.7px;margin:6px 0 0}.weather{display:flex;align-items:center;gap:17px;font-size:10px;color:#aab8ba}.weather span{color:#657b83}.scene-wrap{flex:1;min-height:250px;position:relative;overflow:hidden;border:1px solid #33444b;border-radius:7px;background:#96b0b7}.scene-wrap canvas{display:block;width:100%;height:100%}#view{position:absolute;inset:0}.scene-top{position:absolute;top:20px;left:20px;right:20px;display:flex;justify-content:space-between;pointer-events:none}.location{font-size:10px;letter-spacing:2px;text-shadow:0 1px 4px #000}.location small{display:block;font-size:9px;letter-spacing:1px;margin-top:7px;color:#e5eeed99}.view-controls{display:flex;gap:5px;pointer-events:auto}.chip{border:1px solid #ffffff36;border-radius:3px;background:#0a1a226b;font-size:9px;padding:7px 10px;backdrop-filter:blur(8px)}.chip.on{color:var(--lime);border-color:#d5f66c66}.scene-bottom{position:absolute;bottom:18px;left:20px;right:20px;display:flex;justify-content:space-between;align-items:flex-end}.scene-tag{font-size:9px;letter-spacing:1.5px;padding:7px 9px;border:1px solid #ffffff30;background:#0b1b2355;border-radius:3px}.scene-tag .dot{width:5px;height:5px}.camera-tag{font-size:9px;color:#d8e4e6aa}.intro{position:absolute;left:26px;bottom:67px;max-width:430px;text-shadow:0 2px 14px #00151b;pointer-events:none}.intro .eyebrow{color:#e2f5d1;font-size:10px}.intro h2{font-size:35px;font-weight:500;letter-spacing:-1px;margin:8px 0}.intro p{font-size:11px;line-height:1.7;color:#e0e8e8;max-width:330px;margin:0}.telemetry{display:grid;grid-template-columns:repeat(6,1fr);gap:1px;border:1px solid var(--line);background:var(--line);border-radius:5px;overflow:hidden}.metric{padding:16px 17px 14px;background:#101b20}.metric .eyebrow{font-size:8px;letter-spacing:1.2px}.metric .num{font-family:'Barlow Condensed',Arial,sans-serif;font-size:29px;margin-top:8px;font-weight:500;line-height:1}.metric .unit{font-family:Inter,Arial,sans-serif;font-size:9px;color:#7d929a;margin-left:5px}.metric .sub{margin-top:9px;font-size:8px;color:#6e858d}.metric:last-child .num{color:var(--lime)}.bottom{display:flex;justify-content:space-between;align-items:center;gap:10px;font-size:9px;color:#7d929a}.keys{display:flex;gap:14px;flex-wrap:wrap}.keys span{white-space:nowrap}kbd{font:9px Inter,Arial;border:1px solid #35444b;color:#bbcacd;padding:3px 5px;border-radius:3px;margin-right:5px}.bottom button{border:0;background:none;color:#a0b2b7;font-size:10px;padding:4px}.hud{position:absolute;inset:0;pointer-events:none;color:#deff9a;text-shadow:0 1px 3px #102817;font-family:monospace;display:none}.cross{position:absolute;left:50%;top:48%;width:90px;height:18px;transform:translate(-50%,-50%);border-top:1px solid #d5f66ca0}.cross:after{content:'';position:absolute;left:44px;top:-6px;height:12px;border-left:1px solid #d5f66c}.hud-speed{position:absolute;left:9%;top:48%;font-size:18px;border-bottom:1px solid #d5f66c;padding:7px}.hud-alt{position:absolute;right:9%;top:48%;font-size:18px;border-bottom:1px solid #d5f66c;padding:7px}.heading{position:absolute;top:65px;left:50%;transform:translateX(-50%);font-size:11px;letter-spacing:4px}.notice{position:absolute;left:50%;top:105px;transform:translateX(-50%);padding:8px 14px;border-radius:3px;background:#101f28aa;color:#edf5e1;font-size:10px;text-align:center;max-width:80%}.mission-live{position:absolute;left:20px;top:65px;font-size:10px;color:#eef4ef;line-height:1.9;display:none}.overlay{position:fixed;inset:0;background:#050c11bb;z-index:10;display:none;align-items:center;justify-content:center;backdrop-filter:blur(8px)}.modal{width:480px;max-width:90vw;padding:30px;border:1px solid #425458;background:#101c22;border-radius:8px}.modal h2{margin-top:0}.modal p{color:#a9bbbf;line-height:1.8;font-size:12px}.control-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:24px 0;color:#9cafb4;font-size:11px}.modal button{background:var(--lime);color:#152016;border:0;padding:12px 20px;border-radius:4px}.loading{position:absolute;right:20px;bottom:56px;font-size:9px;color:#dae9e8;opacity:.7}
@media(min-height:850px){.plane-card{padding:14px}.plane-sil{height:45px}.main{gap:23px}}@media(max-height:850px) and (min-width:651px){aside{padding:18px 23px}.aside-heading{font-size:21px;margin:7px 0 14px}.section-label{margin:16px 0 9px}.plane-card{padding:11px}.plane-sil{height:35px;margin:7px 0 4px}.mode{padding:10px}.launch{margin-top:10px;padding:13px}.side-foot{margin-top:10px}}@media(max-width:950px){.layout{grid-template-columns:250px 1fr}aside{padding:20px 15px}.main{padding:20px 16px}.weather{display:none}.metric{padding:13px 9px}.keys{gap:7px}nav{gap:15px}.intro h2{font-size:28px}}@media(max-width:650px){header{height:60px;padding:0 16px}nav{display:none}.sys{font-size:8px}.layout{height:calc(100vh - 60px);grid-template-columns:1fr}aside{position:absolute;z-index:3;left:12px;top:128px;width:205px;max-height:calc(100vh - 225px);padding:13px;background:#0e171bef;border:1px solid #425255;border-radius:5px}.aside-heading,.plane-sil,.side-foot{display:none}.section-label{margin:10px 0;font-size:8px}.plane-card{padding:9px}.plane-card h3{font-size:11px}.plane-card p{font-size:8px}.specs{margin-top:7px;font-size:8px}.mode{padding:8px}.mode strong{font-size:10px}.mode small{font-size:8px}.launch{padding:11px;margin-top:5px}.main{padding:15px 12px;gap:12px}.topline h1{font-size:20px}.scene-top{top:12px;left:12px;right:12px}.location{display:none}.view-controls{margin-left:auto}.intro{display:none}.telemetry{grid-template-columns:repeat(3,1fr)}.metric{padding:9px}.metric .num{font-size:21px}.metric .sub{display:none}.bottom .keys{display:none}body.flying aside{display:none}}
</style></head><body>
<header><div class="brand"><svg viewBox="0 0 40 40" fill="none"><path d="M20 4L36 33H28L20 18L12 33H4L20 4Z" fill="#d5f66c"/><path d="M20 25L25 34H15Z" fill="#d5f66c"/></svg><div>APEX AERO<small>FLIGHT SIMULATION</small></div></div><nav><span class="active">Flight operations</span><span>Live telemetry</span><span>World exploration</span></nav><div class="sys"><i class="dot"></i>SYSTEMS ONLINE</div></header>
<div class="layout"><aside><div class="eyebrow">Your next horizon</div><h2 class="aside-heading">Prepare for departure.</h2><div class="section-label">01 / SELECT AIRCRAFT <span>03 AVAILABLE</span></div><div id="fleet"></div><div class="section-label">02 / FLIGHT SCENARIO</div><button class="mode selected" data-mode="precision"><i>↗</i><div><strong>Precision flight</strong><small>Takeoff · approach · landing</small></div></button><button class="mode" data-mode="dogfight"><i>⌖</i><div><strong>Dogfight</strong><small>Intercept · maneuver · engage</small></div></button><button class="launch" id="launch">BEGIN FLIGHT <span>↗</span></button><div class="side-foot"><span>NEWTONIAN FLIGHT MODEL<br>ISA STANDARD ATMOSPHERE</span><span>v.1.0<br>60 Hz</span></div></aside>
<main class="main"><div class="topline"><div><div class="eyebrow">Flight operations / free flight</div><h1>The sky is not the limit.</h1></div><div class="weather">☀ &nbsp; CLEAR SKIES <span>|</span> 18°C <span>|</span> WIND 240° / 6 KT</div></div><div class="scene-wrap" id="scene"><div id="view"></div><div class="scene-top"><div class="location">NORTH COAST RANGE<small>47° 36′ N &nbsp; 122° 20′ W &nbsp; / &nbsp; PROCEDURAL WORLD</small></div><div class="view-controls"><button class="chip on" id="camera">CHASE VIEW</button><button class="chip" id="audio">SOUND OFF</button><button class="chip" id="pause">Ⅱ</button></div></div><div class="intro" id="intro"><div class="eyebrow">A WORLD WITHOUT BOUNDARIES</div><h2>Find your altitude.</h2><p>Three aircraft. Two missions. One uncompromising flight model. Take the controls and feel every force.</p></div><div class="hud" id="hud"><div class="heading" id="heading">270 &nbsp; | &nbsp; 280 &nbsp; | &nbsp; 290</div><div class="cross" id="cross"></div><div class="hud-speed" id="hudSpeed">0</div><div class="hud-alt" id="hudAlt">0</div><div class="notice" id="notice">Increase throttle to begin your takeoff roll.</div></div><div class="mission-live" id="missionLive"></div><div class="scene-bottom"><div class="scene-tag"><i class="dot"></i><span id="status">READY FOR DEPARTURE</span></div><div class="camera-tag" id="camLabel">EXTERNAL CAMERA &nbsp; / &nbsp; 01</div></div><div class="loading" id="engineStatus">INITIALIZING 3D ENGINE</div></div><div class="telemetry"><div class="metric"><div class="eyebrow">AIRSPEED</div><div class="num"><span id="speed">000</span><span class="unit">KTS</span></div><div class="sub">TRUE AIRSPEED</div></div><div class="metric"><div class="eyebrow">ALTITUDE</div><div class="num"><span id="alt">000</span><span class="unit">FT</span></div><div class="sub">ABOVE SEA LEVEL</div></div><div class="metric"><div class="eyebrow">VERTICAL SPEED</div><div class="num"><span id="vs">0</span><span class="unit">FPM</span></div><div class="sub">RATE OF CLIMB</div></div><div class="metric"><div class="eyebrow">ATTITUDE</div><div class="num"><span id="pitch">0</span><span class="unit">° PITCH</span></div><div class="sub" id="roll">ROLL &nbsp; 0.0°</div></div><div class="metric"><div class="eyebrow">ENGINE</div><div class="num"><span id="rpm">0</span><span class="unit">% RPM</span></div><div class="sub" id="throttle">THROTTLE &nbsp; 0%</div></div><div class="metric"><div class="eyebrow">LOAD FACTOR</div><div class="num"><span id="g">1.0</span><span class="unit">G</span></div><div class="sub" id="aero">FLIGHT ENVELOPE NORMAL</div></div></div><div class="bottom"><div class="keys"><span><kbd>W S</kbd>Pitch</span><span><kbd>A D</kbd>Roll</span><span><kbd>Q E</kbd>Yaw</span><span><kbd>↑ ↓</kbd>Throttle</span><span><kbd>SPACE</kbd>Fire / Brake</span></div><button id="help">ⓘ &nbsp; Flight controls</button></div></main></div>
<div class="overlay" id="helpModal"><div class="modal"><div class="eyebrow">PILOT REFERENCE</div><h2>You're in control.</h2><p>Increase throttle with ↑. At rotation speed, hold S to lift the nose. Lift depends on airspeed and angle of attack — pulling too hard can stall the wings. Bank to turn; use rudder to coordinate.</p><div class="control-grid"><span><kbd>W / S</kbd> Nose down / up</span><span><kbd>A / D</kbd> Roll left / right</span><span><kbd>Q / E</kbd> Rudder</span><span><kbd>↑ / ↓</kbd> Throttle</span><span><kbd>C</kbd> Camera</span><span><kbd>P</kbd> Pause</span><span><kbd>M</kbd> Mouse flight toggle</span><span><kbd>R</kbd> Restart mission</span><span><kbd>Space</kbd> Brake / cannon</span><span><kbd>F</kbd> Flaps</span></div><p id="physicsInfo">Fixed-step rigid-body dynamics · lift/drag polar · post-stall aerodynamics · altitude-dependent density · engine spool · ground contact and friction. Aircraft are illustrative, not certified training models.</p><button id="closeHelp">BACK TO FLIGHT</button></div></div>
<script>
const $=id=>document.getElementById(id);
const fleet=[{name:'Cessna 172',role:'LIGHT TRAINER / SINGLE ENGINE',speed:126,tag:'STABLE',mass:1110,area:16.2,span:11,thrust:2650,cd:.028,k:.052,cla:5.4,stall:.29,rot:52,color:0xe3e5d8,type:0,inertia:[1450,2300,2900],power:1},{name:'P-51 Mustang',role:'WARBIRD / HIGH PERFORMANCE',speed:380,tag:'AGILE',mass:4200,area:21.8,span:11.3,thrust:12500,cd:.021,k:.047,cla:5.1,stall:.27,rot:95,color:0x9baeb1,type:1,inertia:[8200,13500,18000],power:1.4},{name:'F-16 Falcon',role:'MULTIROLE / JET FIGHTER',speed:1320,tag:'SUPERSONIC',mass:11000,area:27.9,span:9.8,thrust:108000,cd:.023,k:.10,cla:4.2,stall:.36,rot:145,color:0x7d929e,type:2,inertia:[18000,60000,70000],power:2}];
let selected=0,mode='precision',running=false,paused=false,camMode=0,muted=true,mouseFlight=false,flaps=false,THREE,world;
const sil='<svg class="plane-sil" viewBox="0 0 240 65"><path d="M119 2L126 8L128 27L210 42L211 48L129 40L127 53L150 59L150 63L121 60L93 63L93 59L114 53L112 40L29 48L30 42L113 27L114 8Z" fill="currentColor"/></svg>';
$('fleet').innerHTML=fleet.map((p,i)=>`<div class="plane-card ${i===0?'selected':''}" data-plane="${i}"><div class="row"><div><h3>${p.name}</h3><p>${p.role}</p></div><span class="check">${i===0?'◉':'○'}</span></div>${sil.replace('currentColor',i===0?'#d5f66c':i===1?'#9caeb4':'#657e8a')}<div class="specs"><span><b>${p.speed}</b> KT MAX</span><span><b>${(p.mass/1000).toFixed(1)}</b> T</span><span>${p.tag}</span></div></div>`).join('');
document.querySelectorAll('[data-plane]').forEach(el=>el.onclick=()=>{selected=+el.dataset.plane;document.querySelectorAll('[data-plane]').forEach((e,i)=>{e.classList.toggle('selected',i===selected);e.querySelector('.check').textContent=i===selected?'◉':'○'});if(world&&!running)world.preview()});
document.querySelectorAll('[data-mode]').forEach(el=>el.onclick=()=>{mode=el.dataset.mode;document.querySelectorAll('[data-mode]').forEach(e=>e.classList.toggle('selected',e===el))});
$('help').onclick=()=>{$('helpModal').style.display='flex';if(running)paused=true};$('closeHelp').onclick=()=>{$('helpModal').style.display='none';paused=false};
$('launch').onclick=()=>{if(!world){$('engineStatus').textContent='Loading Three.js… please wait';return}world.start()};
$('pause').onclick=()=>{paused=!paused;$('pause').textContent=paused?'▶':'Ⅱ'};
$('camera').onclick=()=>{camMode=(camMode+1)%3;$('camera').textContent=['CHASE VIEW','COCKPIT VIEW','ORBIT VIEW'][camMode];$('camLabel').textContent=['EXTERNAL CAMERA / 01','PILOT CAMERA / 02','ORBIT CAMERA / 03'][camMode]};
$('audio').onclick=()=>{muted=!muted;if(!muted)audio.init();$('audio').textContent=muted?'SOUND OFF':'SOUND ON';$('audio').classList.toggle('on',!muted)};
const keys={};window.addEventListener('keydown',e=>{if(['ArrowUp','ArrowDown',' ','w','s','a','d','q','e'].includes(e.key))e.preventDefault();keys[e.key.toLowerCase()]=true;if(e.repeat)return;if(e.key.toLowerCase()==='c')$('camera').click();if(e.key.toLowerCase()==='p')$('pause').click();if(e.key.toLowerCase()==='r'&&world)world.start();if(e.key.toLowerCase()==='m')mouseFlight=!mouseFlight;if(e.key.toLowerCase()==='f')flaps=!flaps});window.addEventListener('keyup',e=>keys[e.key.toLowerCase()]=false);window.addEventListener('blur',()=>{Object.keys(keys).forEach(k=>keys[k]=false)});
const pointer={x:0,y:0};$('scene').addEventListener('mousemove',e=>{const r=$('scene').getBoundingClientRect();pointer.x=(e.clientX-r.left)/r.width*2-1;pointer.y=(e.clientY-r.top)/r.height*2-1});
const audio={init(){if(this.ctx){this.ctx.resume();return}const C=window.AudioContext||window.webkitAudioContext;this.ctx=new C();this.gain=this.ctx.createGain();this.gain.gain.value=0;this.gain.connect(this.ctx.destination);this.osc=this.ctx.createOscillator();this.osc.type='sawtooth';this.filter=this.ctx.createBiquadFilter();this.filter.type='lowpass';this.osc.connect(this.filter);this.filter.connect(this.gain);this.osc.start();const b=this.ctx.createBuffer(1,this.ctx.sampleRate*2,this.ctx.sampleRate),d=b.getChannelData(0);let brown=0;for(let i=0;i<d.length;i++){brown=(brown+Math.random()*.04-.02)/1.02;d[i]=brown*5}this.noise=this.ctx.createBufferSource();this.noise.buffer=b;this.noise.loop=true;this.wind=this.ctx.createGain();this.wind.gain.value=0;this.noise.connect(this.wind);this.wind.connect(this.ctx.destination);this.noise.start()},update(rpm,speed,rho,aoa){if(!this.ctx)return;const t=this.ctx.currentTime;this.osc.frequency.setTargetAtTime(25+rpm*(selected===2?145:85),t,.1);this.filter.frequency.setTargetAtTime(100+rpm*650,t,.15);this.gain.gain.setTargetAtTime(muted||!running||paused?0:(.025+rpm*.05)*Math.sqrt(rho/1.225),t,.15);this.wind.gain.setTargetAtTime(muted||!running||paused?0:Math.min(.16,speed/1600+Math.abs(aoa)*.07),t,.2)}};
// An offline scenic preview remains usable while the mandatory Three.js module loads.
function fallback(){const c=document.createElement('canvas');c.id='fallback';$('view').append(c);const draw=()=>{const r=$('view').getBoundingClientRect();c.width=r.width*devicePixelRatio;c.height=r.height*devicePixelRatio;const x=c.getContext('2d');x.scale(devicePixelRatio,devicePixelRatio);const w=r.width,h=r.height;let g=x.createLinearGradient(0,0,0,h);g.addColorStop(0,'#749aa7');g.addColorStop(.55,'#c7d2cf');g.addColorStop(1,'#617d7d');x.fillStyle=g;x.fillRect(0,0,w,h);for(let layer=0;layer<5;layer++){x.beginPath();x.moveTo(0,h);for(let i=0;i<=w;i+=4){const y=h*(.43+layer*.1)-Math.abs(Math.sin(i/w*6+layer*2)+.5*Math.sin(i/w*17+layer))*h*(.07+layer*.025);x.lineTo(i,y)}x.lineTo(w,h);x.fillStyle=['#93a9ae','#7e999c','#5e8082','#3e656a','#254b53'][layer];x.fill()}x.fillStyle='#658a98';x.beginPath();x.moveTo(w*.8,h*.6);x.lineTo(w*.45,h);x.lineTo(w,h);x.lineTo(w,h*.6);x.fill();x.save();x.translate(w*.59,h*.52);x.rotate(-.08);x.fillStyle='#dce3da';x.shadowColor='#15343c';x.shadowBlur=12;x.beginPath();x.moveTo(-8,-72);x.lineTo(8,-72);x.lineTo(15,-12);x.lineTo(135,15);x.lineTo(133,27);x.lineTo(12,13);x.lineTo(7,58);x.lineTo(40,72);x.lineTo(37,80);x.lineTo(0,68);x.lineTo(-37,80);x.lineTo(-40,72);x.lineTo(-7,58);x.lineTo(-12,13);x.lineTo(-133,27);x.lineTo(-135,15);x.lineTo(-15,-12);x.closePath();x.fill();x.fillStyle='#304e59';x.fillRect(-6,-42,12,26);x.restore()};draw();window.addEventListener('resize',draw)}fallback();
(async()=>{try{THREE=await import('https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js');initWorld();$('fallback')?.remove();$('engineStatus').textContent='THREE.JS / PHYSICS READY';setTimeout(()=>$('engineStatus').style.display='none',2500)}catch(e){$('engineStatus').textContent='3D engine requires a network connection';console.warn('Three.js CDN unavailable',e.message)}})();
function initWorld(){
const T=THREE,scene=new T.Scene();scene.background=new T.Color(0x9bb9c6);scene.fog=new T.FogExp2(0xb6c9cd,.000065);const renderer=new T.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,1.7));renderer.setSize($('view').clientWidth,$('view').clientHeight);renderer.outputColorSpace=T.SRGBColorSpace;renderer.toneMapping=T.ACESFilmicToneMapping;renderer.toneMappingExposure=1.15;renderer.shadowMap.enabled=true;renderer.shadowMap.type=T.PCFSoftShadowMap;$('view').append(renderer.domElement);
const camera=new T.PerspectiveCamera(53,1,1,65000);scene.add(new T.HemisphereLight(0xd3e5f1,0x33472e,2.1));const sun=new T.DirectionalLight(0xffe3b5,3.1);sun.position.set(-1800,3400,1400);scene.add(sun);
const clamp=T.MathUtils.clamp;function terrainHeight(x,z){const coast=clamp((x+1700)/1800,0,1);const a=Math.sin(x*.00047+Math.sin(z*.00034))*Math.cos(z*.00043);const b=Math.sin(x*.0011+z*.0006)*Math.cos(z*.00085);let h=-45+coast*(140+Math.pow(Math.abs(a),2)*1650+Math.abs(b)*260+Math.sin(x*.003+z*.002)*35);const runway=clamp(Math.max((Math.abs(x)-160)/500,(Math.abs(z)-1150)/650),0,1);return 28*(1-runway)+h*runway}
const geo=new T.PlaneGeometry(44000,44000,260,260);geo.rotateX(-Math.PI/2);const pos=geo.attributes.position,colors=[];for(let i=0;i<pos.count;i++){const x=pos.getX(i),z=pos.getZ(i),y=terrainHeight(x,z);pos.setY(i,y);let c=new T.Color();if(y>1300)c.set(0xb4b8b1);else if(y>780)c.set(0x737e72);else if(y>160)c.set(0x446450);else c.set(0x6d8051);const n=(Math.sin(x*.018)*Math.cos(z*.019)+Math.sin(x*.039+z*.027))*.028;c.offsetHSL(0,0,n);colors.push(c.r,c.g,c.b)}geo.setAttribute('color',new T.Float32BufferAttribute(colors,3));geo.computeVertexNormals();const texCanvas=document.createElement('canvas');texCanvas.width=texCanvas.height=128;const tx=texCanvas.getContext('2d'),im=tx.createImageData(128,128);for(let i=0;i<im.data.length;i+=4){let n=180+Math.random()*75;im.data[i]=im.data[i+1]=im.data[i+2]=n;im.data[i+3]=255}tx.putImageData(im,0,0);const tex=new T.CanvasTexture(texCanvas);tex.wrapS=tex.wrapT=T.RepeatWrapping;tex.repeat.set(220,220);scene.add(new T.Mesh(geo,new T.MeshStandardMaterial({vertexColors:true,map:tex,roughness:.94})));
const ocean=new T.Mesh(new T.PlaneGeometry(65000,65000),new T.MeshStandardMaterial({color:0x377787,roughness:.24,metalness:.42}));ocean.rotation.x=-Math.PI/2;ocean.position.y=0;scene.add(ocean);
function box(w,h,d,col,x,y,z){const m=new T.Mesh(new T.BoxGeometry(w,h,d),new T.MeshStandardMaterial({color:col,roughness:.8}));m.position.set(x,y,z);scene.add(m);return m}box(70,.35,2100,0x38434a,0,28.3,0);for(let z=-990;z<1000;z+=65){box(1,.1,26,0xf2eee2,0,28.55,z);box(.7,.1,50,0xc4c8be,-32,28.55,z);box(.7,.1,50,0xc4c8be,32,28.55,z)}for(let x=-24;x<25;x+=8){box(3,.1,70,0xf3f0e6,x,28.55,880);box(3,.1,70,0xf3f0e6,x,28.55,-880)}box(130,.3,350,0x697574,135,28.2,120);for(let i=0;i<4;i++)box(40,16,40,0x9caba7,185,36,40+i*65);box(14,45,14,0x849a9d,110,50,-140);box(25,10,22,0x3f5961,110,77,-140);
const treeGeo=new T.ConeGeometry(6,30,5),treeMat=new T.MeshStandardMaterial({color:0x294d3e,roughness:1});const trees=new T.InstancedMesh(treeGeo,treeMat,1700),dummy=new T.Object3D();let seed=11;function rand(){seed=(seed*1664525+1013904223)>>>0;return seed/4294967296}for(let i=0;i<1700;i++){let x=rand()*12500-1800,z=rand()*18000-9000,h=terrainHeight(x,z);if(h<30||h>1050||Math.abs(x)<230&&Math.abs(z)<1300){dummy.position.set(0,-1000,0)}else dummy.position.set(x,h+13,z);const s=.6+rand()*1.2;dummy.scale.set(s,s,s);dummy.updateMatrix();trees.setMatrixAt(i,dummy.matrix)}scene.add(trees);
function makePlane(p,enemy=false){const g=new T.Group(),paint=new T.MeshStandardMaterial({color:enemy?0x754c43:p.color,metalness:.48,roughness:.32}),dark=new T.MeshStandardMaterial({color:0x163640,metalness:.5,roughness:.18}),trim=new T.MeshStandardMaterial({color:p.type===0?0xb84932:0x273a42,metalness:.3,roughness:.4});function part(geo,mat,x,y,z){const m=new T.Mesh(geo,mat);m.position.set(x,y,z);g.add(m);return m}const fus=part(new T.SphereGeometry(1,24,16),paint,0,0,0);fus.scale.set(p.type===2?.75:.62,.72,p.type===2?6.6:4.1);const canopy=part(new T.SphereGeometry(1,20,14),dark,0,.65,-1);canopy.scale.set(.53,.55,p.type===2?1.6:1.2);
function wing(span,chord,z,y,sweep){const shape=new T.Shape();shape.moveTo(-span/2,z+chord*.45+sweep);shape.lineTo(-span/2,z-chord*.45+sweep);shape.lineTo(-.5,z-chord);shape.lineTo(.5,z-chord);shape.lineTo(span/2,z-chord*.45+sweep);shape.lineTo(span/2,z+chord*.45+sweep);shape.lineTo(.5,z+chord);shape.lineTo(-.5,z+chord);shape.closePath();const geo=new T.ExtrudeGeometry(shape,{depth:.13,bevelEnabled:true,bevelSize:.04,bevelThickness:.04,bevelSegments:1});geo.rotateX(Math.PI/2);const m=new T.Mesh(geo,paint);m.position.y=y;g.add(m)}wing(p.span,p.type===2?2.1:.85,p.type===2?.6:0,p.type===0?.65:0,p.type===2?1.8:.25);wing(p.type===2?5:4, .65, p.type===2?4.4:3.1,.2,.4);const fin=part(new T.BoxGeometry(.13,1.8,1.5),trim,0,1,p.type===2?4.6:3.2);fin.rotation.x=-.3;part(new T.CylinderGeometry(.3,.4,.6,16),trim,0,0,p.type===2?6.25:-3.8).rotation.x=Math.PI/2;
if(p.type!==2){const prop=new T.Group();const blade=new T.Mesh(new T.BoxGeometry(.14,3.3,.07),trim);prop.add(blade);prop.position.z=-4.2;g.add(prop);g.userData.prop=prop}if(p.type===0){for(const x of [-1,1]){const strut=part(new T.BoxGeometry(.07,1.5,.07),trim,x,-.7,1);strut.rotation.z=x*.3;const wheel=part(new T.CylinderGeometry(.28,.28,.2,16),trim,x*1.2,-1.45,1);wheel.rotation.z=Math.PI/2}const nose=part(new T.CylinderGeometry(.21,.21,.17,12),trim,0,-1.3,-2.6);nose.rotation.z=Math.PI/2}return g}
let airplane=makePlane(fleet[0]);scene.add(airplane);const enemy=makePlane(fleet[1],true);scene.add(enemy);enemy.visible=false;
const state={pos:new T.Vector3(0,31,770),vel:new T.Vector3(),q:new T.Quaternion(),omega:new T.Vector3(),throttle:0,rpm:0,rho:1.225,aoa:0,speed:0,g:1,pitch:0,roll:0,damage:0,ground:true,dead:false,elapsed:0,kills:0,ammo:250};let enemyHP=100,shots=[],shootTimer=0,missionStage=0,landingTime=0;
const up=new T.Vector3(0,1,0),forward=new T.Vector3(0,0,-1),right=new T.Vector3(1,0,0);const airWind=new T.Vector3(2.2,0,1.1);const ep=new T.Vector3();
world={state,terrainHeight,renderer,scene,preview(){scene.remove(airplane);airplane=makePlane(fleet[selected]);scene.add(airplane)},start(){world.preview();Object.keys(keys).forEach(k=>keys[k]=false);running=true;paused=false;flaps=false;state.q.identity();state.omega.set(0,0,0);state.throttle=mode==='dogfight'?.7:0;state.rpm=state.throttle;state.pos.set(mode==='dogfight'?150:0,mode==='dogfight'?1700:30.1,mode==='dogfight'?5000:790);state.vel.set(0,0,mode==='dogfight'?-Math.max(80,fleet[selected].rot*.8):0);state.damage=0;state.dead=false;state.elapsed=0;state.ammo=250;state.kills=0;missionStage=0;landingTime=0;enemyHP=100;enemy.visible=mode==='dogfight';enemy.position.copy(state.pos).add(new T.Vector3(150,60,-900));shots.forEach(s=>scene.remove(s.mesh));shots=[];$('intro').style.display='none';$('hud').style.display='block';$('missionLive').style.display='block';$('launch').innerHTML='RESTART FLIGHT <span>↻</span>';$('status').textContent='FLIGHT IN PROGRESS';document.body.classList.add('flying');camera.position.copy(state.pos).add(new T.Vector3(0,7,24));audio.init()},step:physics};
function physics(dt){if(state.dead)return;const p=fleet[selected];state.elapsed+=dt;let pitchIn=(keys.s?1:0)-(keys.w?1:0),rollIn=(keys.a?1:0)-(keys.d?1:0),yawIn=(keys.q?1:0)-(keys.e?1:0);if(mouseFlight){pitchIn=clamp(-pointer.y,-1,1);rollIn=clamp(-pointer.x,-1,1)}state.throttle=clamp(state.throttle+((keys.arrowup?1:0)-(keys.arrowdown?1:0))*dt*.24,0,1);state.rpm+=(state.throttle-state.rpm)*(1-Math.exp(-dt/(p.type===2?2.2:1.2)));
const f=forward.clone().applyQuaternion(state.q),u=up.clone().applyQuaternion(state.q),r=right.clone().applyQuaternion(state.q);const av=state.vel.clone().sub(airWind);const speed=Math.max(av.length(),.01);const vf=av.dot(f),vu=av.dot(u),vr=av.dot(r);const aoa=Math.atan2(-vu,Math.max(1,vf));const beta=Math.atan2(vr,Math.max(1,vf));state.aoa=aoa;state.speed=speed;state.rho=1.225*Math.pow(Math.max(.18,1-state.pos.y*.0000225577),4.256);const pressure=.5*state.rho*speed*speed;
let cl=.24+p.cla*aoa+(flaps?.38:0);if(Math.abs(aoa)>p.stall){const peak=.24+p.cla*p.stall;cl=Math.sign(aoa)*peak*Math.max(.12,Math.cos(Math.min(Math.PI/2,(Math.abs(aoa)-p.stall)*2.6)))*Math.exp(-(Math.abs(aoa)-p.stall)*1.5)}cl=clamp(cl,-1.5,1.9);const cd=p.cd+p.k*cl*cl+(flaps?.04:0)+Math.max(0,Math.abs(aoa)-p.stall)*.9+Math.max(0,speed/340-.78)*.10;
const vdir=av.clone().normalize(),liftDir=r.clone().cross(vdir).normalize();let lift=pressure*p.area*cl;if(state.ground&&vf<8)lift=0;const force=liftDir.multiplyScalar(lift).addScaledVector(vdir,-pressure*p.area*cd);force.addScaledVector(r,-pressure*p.area*.55*beta);const thrust=p.thrust*state.rpm*(p.type===2?Math.pow(state.rho/1.225,.6):Math.pow(state.rho/1.225,.85)*clamp(1-speed/(p.speed*.514*1.35),.15,1));force.addScaledVector(f,thrust);state.g=force.dot(u)/(p.mass*9.81);force.y-=p.mass*9.81;
// Body-axis moments: elevator/aileron/rudder authority, static stability, aerodynamic rate damping, Euler rigid-body coupling.
const authority=clamp(pressure/1800,0,4);const torque=new T.Vector3(pitchIn*p.inertia[0]*.7*authority-p.inertia[0]*aoa*.85*authority, yawIn*p.inertia[1]*.38*authority+p.inertia[1]*beta*.35*authority,rollIn*p.inertia[2]*1.15*authority);const I=new T.Vector3(...p.inertia),angMom=state.omega.clone().multiply(I),gyroscopic=state.omega.clone().cross(angMom);torque.sub(gyroscopic);torque.x-=I.x*state.omega.x*(.8+authority*.7);torque.y-=I.y*state.omega.y*(.55+authority*.8);torque.z-=I.z*state.omega.z*(.9+authority*.55);state.omega.addScaledVector(torque.divide(I),dt);state.omega.clampLength(0,2.6);const axis=state.omega.clone();const rate=axis.length();if(rate>0)state.q.multiply(new T.Quaternion().setFromAxisAngle(axis.normalize(),rate*dt)).normalize();state.vel.addScaledVector(force,dt/p.mass);state.pos.addScaledVector(state.vel,dt);
const ground= Math.max(0,terrainHeight(state.pos.x,state.pos.z))+1.8;state.ground=state.pos.y<=ground+.05;const onRunway=Math.abs(state.pos.x)<34&&Math.abs(state.pos.z)<1050;
if(state.pos.y<ground){const sink=-state.vel.y;const eul=new T.Euler().setFromQuaternion(state.q,'YXZ');if(sink>5||Math.abs(eul.z)>.45||Math.abs(eul.x)>.38||(!onRunway&&speed>22)){crash('AIRFRAME LOST · Terrain impact. Press R to retry.');return}state.pos.y=ground;state.vel.y=Math.max(0,state.vel.y);const horizontal=new T.Vector3(state.vel.x,0,state.vel.z);let friction=(onRunway?.019:.22)*p.mass*9.81+(keys[' ']?p.mass*3.6:0);if(horizontal.length()>0){const decrease=Math.min(horizontal.length(),friction/p.mass*dt);state.vel.addScaledVector(horizontal.normalize(),-decrease)}state.vel.addScaledVector(r,-state.vel.dot(r)*Math.min(1,dt*5));if(pitchIn<=0||speed<p.rot*.514){const target=new T.Quaternion().setFromEuler(new T.Euler(0,eul.y,0));state.q.slerp(target,dt*3);state.omega.x*=.94;state.omega.z*=.9}state.omega.y+=yawIn*dt*.12;if(missionStage===1&&onRunway&&sink<4){landingTime+=dt;if(landingTime>2&&speed<12){missionStage=2;$('status').textContent='MISSION COMPLETE';notify('PRECISION FLIGHT COMPLETE · Smooth landing. Press R to fly again.')}}}
const euler=new T.Euler().setFromQuaternion(state.q,'YXZ');state.pitch=euler.x*180/Math.PI;state.roll=euler.z*180/Math.PI;if(!state.ground&&state.pos.y-ground>80&&mode==='precision')missionStage=Math.max(1,missionStage);if(Math.abs(state.g)> (selected===0?4.4:selected===1?7:9.5))state.damage+=dt*Math.abs(state.g)*.45;if(state.damage>25)crash('STRUCTURAL FAILURE · Flight load exceeded. Press R to retry.');
if(mode==='dogfight')combat(dt);if(state.pos.length()>29000)notify('WORLD BOUNDARY · Turn toward the coast.');
}
function crash(msg){state.dead=true;$('status').textContent='FLIGHT ENDED';notify(msg)}function notify(msg){$('notice').textContent=msg}
function combat(dt){const target=state.pos.clone().add(new T.Vector3(Math.sin(state.elapsed*.045)*1000,180,-1700));const dir=target.sub(enemy.position).normalize();const enemySpeed=100+20*Math.sin(state.elapsed*.2);enemy.position.addScaledVector(dir,enemySpeed*dt);enemy.position.y=Math.max(enemy.position.y,terrainHeight(enemy.position.x,enemy.position.z)+300);enemy.lookAt(enemy.position.clone().add(dir));enemy.rotateY(Math.PI);enemy.rotateZ(Math.sin(state.elapsed*.4)*.35);shootTimer-=dt;if(keys[' ']&&shootTimer<=0&&state.ammo>0){shootTimer=.085;state.ammo--;const b=new T.Mesh(new T.SphereGeometry(.6,5,4),new T.MeshBasicMaterial({color:0xffed8a}));const dir=forward.clone().applyQuaternion(state.q);b.position.copy(state.pos).addScaledVector(dir,7);scene.add(b);shots.push({mesh:b,vel:dir.multiplyScalar(800).add(state.vel),life:2})}for(let i=shots.length-1;i>=0;i--){let s=shots[i],old=s.mesh.position.clone();s.mesh.position.addScaledVector(s.vel,dt);s.life-=dt;const seg=s.mesh.position.clone().sub(old),t=clamp(enemy.position.clone().sub(old).dot(seg)/seg.lengthSq(),0,1);if(old.addScaledVector(seg,t).distanceTo(enemy.position)<12){enemyHP-=25;s.life=0;notify('HIT CONFIRMED · Target integrity '+Math.max(0,enemyHP)+'%');if(enemyHP<=0){state.kills++;enemyHP=100;enemy.position.copy(state.pos).add(new T.Vector3(400,180,-1800));notify('TARGET NEUTRALIZED · New contact inbound.')}}if(s.life<=0){scene.remove(s.mesh);s.mesh.geometry.dispose();s.mesh.material.dispose();shots.splice(i,1)}}}
function telemetry(){const kts=Math.round(state.speed*1.944),ft=Math.round(state.pos.y*3.281);$('speed').textContent=String(kts).padStart(3,'0');$('alt').textContent=String(ft).padStart(3,'0');$('vs').textContent=Math.round(state.vel.y*196.85/10)*10;$('pitch').textContent=state.pitch.toFixed(1);$('roll').textContent='ROLL '+state.roll.toFixed(1)+'°';$('rpm').textContent=Math.round(state.rpm*100);$('throttle').textContent='THROTTLE '+Math.round(state.throttle*100)+'%'+(flaps?' · FLAPS':'');$('g').textContent=state.g.toFixed(1);$('aero').textContent=Math.abs(state.aoa)>fleet[selected].stall?'STALL · REDUCE PITCH':'FLIGHT ENVELOPE NORMAL';$('hudSpeed').textContent=kts+' KT';$('hudAlt').textContent=ft+' FT';$('cross').style.transform=`translate(-50%,-50%) rotate(${-state.roll}deg)`;const f=forward.clone().applyQuaternion(state.q),hdg=(Math.atan2(f.x,-f.z)*180/Math.PI+360)%360;$('heading').textContent='HDG '+Math.round(hdg).toString().padStart(3,'0')+'°';if(mode==='precision'){$('missionLive').innerHTML=`PRECISION FLIGHT<br>${['01 / TAKEOFF','02 / RETURN & LAND','03 / COMPLETE'][missionStage]}<br>ROTATION ${fleet[selected].rot} KT`;if(!state.dead&&missionStage<2)notify(Math.abs(state.aoa)>fleet[selected].stall&&state.speed>20?'STALL WARNING · Lower nose, add power':missionStage===0?state.speed*1.944>=fleet[selected].rot?'ROTATE · Hold S gently to lift the nose':'Increase throttle ↑ · Rotate at '+fleet[selected].rot+' KT':'Return to the coastal runway · Touch down below 800 FPM')}else{$('missionLive').innerHTML='DOGFIGHT / INTERCEPT<br>CONTACT '+Math.round(state.pos.distanceTo(enemy.position))+' M<br>AMMO '+state.ammo+' · KILLS '+state.kills;if(!state.dead&&state.elapsed<6)notify('Contact ahead · Space to fire · Lead your target')}}
let time=performance.now(),acc=0,ui=0;function loop(now){requestAnimationFrame(loop);const dt=Math.min(.08,(now-time)/1000);time=now;if(running&&!paused){acc+=dt;while(acc>=1/120){physics(1/120);acc-=1/120}}if(!running){const t=now*.00004;airplane.position.set(-600,1450,1500);airplane.rotation.set(.04,-.3,-.13);camera.position.set(-583+Math.sin(t)*8,1458,1534);camera.lookAt(airplane.position.clone().add(new T.Vector3(0,2,-10)))}else{airplane.position.copy(state.pos);airplane.quaternion.copy(state.q);const f=forward.clone().applyQuaternion(state.q),u=up.clone().applyQuaternion(state.q);let desired,look;if(camMode===1){desired=state.pos.clone().addScaledVector(u,1.05).addScaledVector(f,1.8);look=desired.clone().addScaledVector(f,100);airplane.visible=false;camera.up.copy(u)}else{airplane.visible=true;camera.up.set(0,1,0);if(camMode===2)desired=state.pos.clone().add(new T.Vector3(Math.sin(now*.00012)*28,10,Math.cos(now*.00012)*28));else desired=state.pos.clone().addScaledVector(f,-24).add(new T.Vector3(0,7,0));desired.y=Math.max(desired.y,terrainHeight(desired.x,desired.z)+3);look=state.pos.clone().addScaledVector(f,10);camera.position.lerp(desired,1-Math.exp(-dt*5))}if(camMode===1)camera.position.copy(desired);camera.lookAt(look);if(airplane.userData.prop)airplane.userData.prop.rotation.z+=state.rpm*dt*160;audio.update(state.rpm,state.speed,state.rho,state.aoa)}ui+=dt;if(ui>.1){ui=0;if(running)telemetry()}renderer.render(scene,camera)}requestAnimationFrame(loop);function resize(){const w=$('view').clientWidth,h=$('view').clientHeight;renderer.setSize(w,h);camera.aspect=w/h;camera.updateProjectionMatrix()}new ResizeObserver(resize).observe($('view'));resize();
}
</script></body></html>
<!-- agent-meta {"model":"openai/gpt-6.1-sol","provider":"openrouter","persona":"full","sessionId":"61e6e479-fa82-45b5-8fd1-ba478787eb17","tokensIn":272823,"tokensOut":14615,"tokensTotal":287438,"cost":0.22844089999999997,"turns":14,"toolCalls":13,"failedToolCalls":0,"timestamp":"2026-09-29T20:13:13.548Z"} -->