diff --git a/sfm/event_browser.html b/sfm/event_browser.html
index 9be5431..bbd960f 100644
--- a/sfm/event_browser.html
+++ b/sfm/event_browser.html
@@ -717,8 +717,9 @@ function renderWaveform(data) {
// up AND down). Mic + histograms keep default auto-scale (always
// positive values; zero at the bottom).
let yBounds = {};
- const isGeoWaveform = !isHistogram && ch !== 'MicL';
- if (isGeoWaveform) {
+ const isGeo = ch !== 'MicL';
+ if (isGeo && !isHistogram) {
+ // Waveform geo: symmetric around zero for full shape detail.
let absMax = 0;
for (const v of values) {
const a = Math.abs(v);
@@ -726,13 +727,25 @@ function renderWaveform(data) {
}
const padded = (absMax || 1) * 1.10;
yBounds = { min: -padded, max: padded };
+ } else if (isGeo && isHistogram) {
+ // Histogram geo: enforce minimum chart range so quiet events
+ // look quiet (matches BW's near-fixed-scale convention).
+ const HIST_GEO_MIN_INS = 0.05;
+ let p = 0;
+ for (const v of values) { const a = Math.abs(v); if (a > p) p = a; }
+ yBounds = { min: 0, max: Math.max(p * 1.10, HIST_GEO_MIN_INS) };
} else if (ch === 'MicL' && micUnit === 'dBL') {
- // Baseline at noise-floor minimum (matches what we floored
- // null/quiet samples to), top at peak + 5 dB headroom.
+ // Mic dBL: baseline at noise-floor minimum, top at peak + 5 dB.
const peakDbl = (typeof peak === 'number' && isFinite(peak))
? peak + 5
: 100;
yBounds = { min: MIC_DBL_FLOOR, max: Math.max(peakDbl, MIC_DBL_FLOOR + 20) };
+ } else if (ch === 'MicL' && isHistogram && micUnit === 'psi') {
+ // Mic histogram in psi: same minimum-range treatment as geo.
+ const HIST_MIC_MIN_PSI = 0.001;
+ let p = 0;
+ for (const v of values) { const a = Math.abs(v); if (a > p) p = a; }
+ yBounds = { min: 0, max: Math.max(p * 1.10, HIST_MIC_MIN_PSI) };
}
const chart = new Chart(canvas, {
diff --git a/sfm/sfm_webapp.html b/sfm/sfm_webapp.html
index 19a3e05..5021c79 100644
--- a/sfm/sfm_webapp.html
+++ b/sfm/sfm_webapp.html
@@ -2748,8 +2748,9 @@ function _renderScWaveform(data) {
// - Mic (always positive sound pressure) + histograms (per-interval
// peaks, always positive): default auto-scale, zero at the bottom.
let yBounds = {};
- const isGeoWaveform = !isHistogram && ch !== 'MicL';
- if (isGeoWaveform) {
+ const isGeo = ch !== 'MicL';
+ if (isGeo && !isHistogram) {
+ // Waveform geo: symmetric around zero, full zoom to shape detail.
let absMax = 0;
for (const v of values) {
const a = Math.abs(v);
@@ -2757,13 +2758,31 @@ function _renderScWaveform(data) {
}
const padded = (absMax || 1) * 1.10;
yBounds = { min: -padded, max: padded };
+ } else if (isGeo && isHistogram) {
+ // Histogram geo: enforce a minimum chart range so a quiet
+ // 0.005 in/s event renders as ~10% of chart height instead of
+ // filling the panel. Matches BW's near-fixed-scale convention
+ // (their footer is "Geo: 0.002 in/s/div" — a chart-relative scale,
+ // not auto-zoom).
+ const HIST_GEO_MIN_INS = 0.05;
+ let peak = 0;
+ for (const v of values) { const a = Math.abs(v); if (a > peak) peak = a; }
+ yBounds = { min: 0, max: Math.max(peak * 1.10, HIST_GEO_MIN_INS) };
} else if (ch === 'MicL' && micUnit === 'dBL') {
- // Pin baseline at the chart floor (which matches what we flooded
- // null/quiet samples to), top at the actual peak + a few dB headroom.
+ // Mic in dBL — pin baseline at noise-floor minimum (where we floored
+ // quiet samples), top at actual peak + a few dB headroom.
const peakDbl = (typeof chPeak === 'number' && isFinite(chPeak))
? chPeak + 5
: 100;
yBounds = { min: MIC_DBL_FLOOR, max: Math.max(peakDbl, MIC_DBL_FLOOR + 20) };
+ } else if (ch === 'MicL' && isHistogram && micUnit === 'psi') {
+ // Mic histogram in psi — same minimum-range treatment as geo.
+ // 0.001 psi ≈ 110 dBL — typical "loud" mic peak. Quiet events
+ // sit near the bottom.
+ const HIST_MIC_MIN_PSI = 0.001;
+ let peak = 0;
+ for (const v of values) { const a = Math.abs(v); if (a > peak) peak = a; }
+ yBounds = { min: 0, max: Math.max(peak * 1.10, HIST_MIC_MIN_PSI) };
}
_scCharts[ch] = new Chart(canvas, {