MoonFlash / index.html
AK51's picture
Upload 3 files
3c88ab4 verified
Raw History Blame Contribute Delete
10.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lunar Impact Flash Detector</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header class="app-header">
<div class="brand">
<div class="moon-icon" aria-hidden="true"></div>
<div>
<h1>Lunar Impact Flash Detector</h1>
<p class="subtitle">Upload lunar earthshine video &middot; detect transient impact flashes</p>
</div>
</div>
<a class="ref-link" href="https://science.nasa.gov/citizen-science/impact-flash/" target="_blank" rel="noopener">About impact flashes &#8599;</a>
</header>
<main class="layout">
<!-- LEFT: viewer -->
<section class="viewer-panel">
<div id="dropZone" class="drop-zone">
<input type="file" id="fileInput" accept="video/mp4,video/*" hidden />
<div class="drop-inner">
<div class="upload-glyph" aria-hidden="true">&#8682;</div>
<p class="drop-title">Drop an MP4 here or click to browse</p>
<p class="drop-hint">A sample video is preloaded so you can try it right away. Best results with steady, monochrome-ish earthshine footage of the dark limb of the Moon.</p>
</div>
</div>
<div id="stage" class="stage hidden">
<div id="viewport" class="viewport">
<div class="video-wrap" id="videoWrap">
<video id="video" playsinline muted></video>
<canvas id="overlay" class="overlay"></canvas>
</div>
<div class="zoom-controls">
<button id="zoomOutBtn" class="btn btn-sm" title="Zoom out">&#8722;</button>
<span id="zoomLabel" class="zoom-label">100%</span>
<button id="zoomInBtn" class="btn btn-sm" title="Zoom in">&#43;</button>
<button id="zoomResetBtn" class="btn btn-ghost btn-sm" title="Reset zoom">Reset</button>
</div>
</div>
<div class="transport">
<button id="prevFrameBtn" class="btn" title="Previous frame">&#9198;</button>
<button id="playBtn" class="btn" title="Play / Pause">&#9658;</button>
<button id="nextFrameBtn" class="btn" title="Next frame">&#9197;</button>
<input id="seek" class="seek" type="range" min="0" max="1000" value="0" step="1" />
<span id="timeLabel" class="time-label">0:00 / 0:00</span>
<button id="newFileBtn" class="btn btn-ghost" title="Load a different file">Change file</button>
</div>
<div class="frame-readout">
<span id="frameLabel" class="frame-label">frame &ndash;</span>
<span class="frame-hint">Use &#9198; / &#9197; or the &larr; &rarr; arrow keys to step frames. Scroll or use +/&minus; to zoom; drag to pan when zoomed.</span>
</div>
<div class="analysis-bar">
<button id="analyzeBtn" class="btn btn-primary">Analyze for flashes</button>
<div id="progressWrap" class="progress-wrap hidden">
<div class="progress-track"><div id="progressBar" class="progress-bar"></div></div>
<span id="progressText" class="progress-text">0%</span>
<button id="cancelBtn" class="btn btn-ghost btn-sm">Cancel</button>
</div>
</div>
</div>
</section>
<!-- RIGHT: controls + results -->
<aside class="side-panel">
<div class="card">
<button type="button" id="settingsToggle" class="collapse-header" aria-expanded="false" aria-controls="settingsBody">
<h2>Detection settings</h2>
<span class="chevron" aria-hidden="true">&#9662;</span>
</button>
<div id="settingsBody" class="collapse-body collapsed">
<label class="field">
<span class="field-label">Sensitivity
<span class="field-value" id="sensVal">Medium</span>
</span>
<input id="sensitivity" type="range" min="1" max="5" value="3" step="1" />
<span class="field-help">Higher = detects fainter flashes (more false positives).</span>
</label>
<label class="field">
<span class="field-label">Min flash size
<span class="field-value" id="minSizeVal">2 px</span>
</span>
<input id="minSize" type="range" min="1" max="30" value="2" step="1" />
<span class="field-help">Ignore bright blobs smaller than this (removes hot pixels).</span>
</label>
<label class="field">
<span class="field-label">Max flash size
<span class="field-value" id="maxSizeVal">1500 px</span>
</span>
<input id="maxSize" type="range" min="50" max="8000" value="1500" step="50" />
<span class="field-help">Ignore very large brightenings (clouds, focus, whole-limb glare).</span>
</label>
<label class="field">
<span class="field-label">Min flash duration
<span class="field-value" id="minDurVal">0 ms</span>
</span>
<input id="minDur" type="range" min="0" max="500" value="0" step="10" />
<span class="field-help">Ignore flashes shorter than this.</span>
</label>
<label class="field">
<span class="field-label">Max flash duration
<span class="field-value" id="maxDurVal">800 ms</span>
</span>
<input id="maxDur" type="range" min="100" max="3000" value="800" step="50" />
<span class="field-help">Reject persistent lights (satellites, glare) that last longer.</span>
</label>
<label class="field checkbox-field">
<input id="showMarkers" type="checkbox" checked />
<span>Show flash markers</span>
</label>
<label class="field checkbox-field">
<input id="highlightPlayback" type="checkbox" checked />
<span>Highlight flashes during playback</span>
</label>
</div>
</div>
<div class="card results-card">
<div class="results-head">
<h2>Detected flashes <span id="flashCount" class="pill">0</span></h2>
<button id="exportBtn" class="btn btn-ghost btn-sm" disabled>Export CSV</button>
</div>
<div id="emptyState" class="empty-state">No analysis yet. Load a video and click <strong>Analyze</strong>.</div>
<div id="resultsScroll" class="results-scroll">
<table id="resultsTable" class="results-table hidden">
<thead>
<tr>
<th>#</th>
<th>Time / Frame</th>
<th>X, Y</th>
<th>Duration</th>
<th>Size</th>
<th>Peak</th>
<th>Clip</th>
</tr>
</thead>
<tbody id="resultsBody"></tbody>
</table>
</div>
</div>
<div class="card note-card">
<button type="button" id="aboutToggle" class="collapse-header" aria-expanded="false" aria-controls="aboutBody">
<h2>About &amp; how it works</h2>
<span class="chevron" aria-hidden="true">&#9662;</span>
</button>
<div id="aboutBody" class="collapse-body collapsed">
<p>This tool scans a video of the Moon for <strong>impact flashes</strong> &mdash; the brief glints of light produced when a meteoroid strikes the lunar surface. Everything runs in your browser; the video is never uploaded.</p>
<h3 class="about-step">1. Remove the steady background</h3>
<p>The Moon drifts and jitters slightly between frames (tracking error, atmosphere, a little rotation) and the earthshine glow changes slowly. The software keeps a <strong>rolling average of recent frames</strong> as a model of the quiet, slowly-changing scene, then subtracts it from the current frame. What survives is only what suddenly <em>changed</em> &mdash; the steady Moon largely cancels out.</p>
<h3 class="about-step">2. Keep only compact bright spots</h3>
<p>Camera shake smears brightness across wide areas, which looks like change everywhere. A <strong>top-hat filter</strong> subtracts a blurred copy of the difference image so broad, soft glows disappear and only <strong>small, sharply-peaked bright points</strong> remain &mdash; the shape an impact flash makes.</p>
<h3 class="about-step">3. Group pixels into blobs</h3>
<p>Neighbouring bright pixels are joined into blobs. Each blob's position, size, shape and peak brightness are measured.</p>
<h3 class="about-step">4. Reject look-alikes</h3>
<p>A blob is dismissed unless it behaves like a real flash:</p>
<ul class="about-list">
<li><strong>Point-like, not a streak</strong> &mdash; thin, elongated shimmer along the Moon's bright edge is rejected.</li>
<li><strong>Right size</strong> &mdash; not a single hot pixel, not a huge brightening (cloud, focus change, glare).</li>
<li><strong>Brief and transient</strong> &mdash; it must rise sharply and then fade within the duration limits; steady lights (stars, satellites) are rejected.</li>
<li><strong>Strong enough</strong> &mdash; its peak must clear a confidence threshold set by the Sensitivity control.</li>
</ul>
<h3 class="about-step">5. Track and report</h3>
<p>Surviving blobs are linked across consecutive frames into a single event, then reported with the <strong>location, the frame(s) it appears in, duration, size and peak brightness</strong>. The method follows established lunar-flash work such as <a href="https://science.nasa.gov/citizen-science/impact-flash/" target="_blank" rel="noopener">NASA's impact-flash citizen science</a> and the <a href="https://users.aber.ac.uk/atc/alfi.htm" target="_blank" rel="noopener">ALFI project</a>.</p>
<p class="disclaimer">This is a screening tool, not confirmation. A real lunar impact must be verified against a second simultaneous recording to rule out satellites, cosmic-ray hits, and camera noise.</p>
</div>
</div>
<footer class="credit">Created by Andy Kong</footer>
</aside>
</main>
<div id="toast" class="toast hidden"></div>
<script src="detector.js"></script>
<script src="app.js"></script>
</body>
</html>