diff --git a/web/chat.html b/web/chat.html
index b564d93..e142f3c 100644
--- a/web/chat.html
+++ b/web/chat.html
@@ -1150,6 +1150,28 @@ function ensurePC() {
if (!carrierOn) throw new Error('start carrier first');
pc = new RTCPeerConnection(RTC_CONFIG);
for (const tr of outboundStream.getTracks()) pc.addTrack(tr, outboundStream);
+ /* Force a memoryless codec (G.711 PCMU/PCMA, else G.722) ahead of Opus.
+ * Opus is a perceptual codec that re-quantizes in 20ms frames and smears the
+ * MARK/SPACE bit-edges this modem rides on; G.711 companding is per-sample,
+ * preserving the carrier envelope like the PulseAudio path. */
+ try {
+ const caps = (RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities('audio')) || null;
+ if (caps && caps.codecs) {
+ const rank = (mime) => {
+ const m = (mime || '').toLowerCase();
+ if (m.endsWith('/pcmu')) return 0;
+ if (m.endsWith('/pcma')) return 1;
+ if (m.endsWith('/g722')) return 2;
+ if (m.includes('opus')) return 99;
+ return 50;
+ };
+ const ordered = caps.codecs.slice().sort((a, b) => rank(a.mimeType) - rank(b.mimeType));
+ for (const t of pc.getTransceivers()) {
+ if (t.setCodecPreferences) { try { t.setCodecPreferences(ordered); } catch (_) {} }
+ }
+ logLine('sys', 'codec preference: G.711/G.722 ahead of Opus (clean amplitude)');
+ }
+ } catch (_) {}
pc.ontrack = (ev) => {
const stream = ev.streams[0] || new MediaStream([ev.track]);
attachInboundTrack(stream, ev.track.id);