Asking
Only logged in members can reply and interact with the post.
Join SimilarWorlds for FREE »

Help me with my renderer.js code it hates meeee </3

window.addEventListener('DOMContentLoaded', () => {
console.log("✅ DOM loaded, checking preload bridge");

// 🌟 Element references
const fpsDisplay = document.getElementById('fps-display');
const ctx = document.getElementById('fpsChart').getContext('2d');
const previewVideo = document.getElementById('preview');
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const exportBtn = document.getElementById('exportBtn');
const recordBtn = document.getElementById('recordBtn');
const recordStatus = document.getElementById('recordStatus');
const sourcePicker = document.getElementById('sourcePicker');
const sessionTimer = document.getElementById('sessionTimer');
const historyBtn = document.getElementById('historyBtn');
const historyPanel = document.getElementById('historyPanel');

// 📜 Session history
const history = JSON.parse(localStorage.getItem('recordingHistory')) || [];

function addSessionEntry(entry) {
history.push(entry);
localStorage.setItem('recordingHistory', JSON.stringify(history));
}

historyBtn.onclick = () => {
const storedHistory = JSON.parse(localStorage.getItem('recordingHistory')) || [];
historyPanel.innerHTML = storedHistory.map(entry => `
<div class="history-entry">
<strong>${entry.windowName}</strong> — ${entry.duration}<br>
📈 Max FPS: ${entry.maxFPS} &nbsp; 📉 Min FPS: ${entry.minFPS}<br>
📤 Exported: ${entry.exportTime}
</div>
`).join('');
historyPanel.classList.toggle('hidden');
};

// 🔧 Buffers & state
const fpsLog = [];
const timestamps = [];
const pointColors = [];
let intervalID = null;
let monitorInterval = null;
let isRecording = false;
let mediaRecorder = null;
let recordedChunks = [];
let timerInterval = null;
let startTime = null;

// 📈 Setup chart
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: timestamps,
datasets: [{
label: 'FPS',
data: fpsLog,
pointBackgroundColor: pointColors,
borderColor: 'rgba(100,100,100,0.5)',
backgroundColor: 'rgba(150,200,255,0.2)',
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
x: { title: { display: true, text: 'Time' } },
y: { title: { display: true, text: 'Frames per Second' }, min: 0, max: 100 }
}
}
});

window.addEventListener('resize', () => chart.resize());

// 🔁 Preload bridge retry
function tryListSources(attempt = 1) {
if (!window.electronAPI?.getSources) {
console.warn(`⚠ Bridge not ready (attempt ${attempt})`);
setTimeout(() => tryListSources(attempt + 1), 500);
return;
}
listSources();
}

tryListSources();

// 🖥 List sources
async function listSources() {
sourcePicker.classList.add('loading');
sourcePicker.innerHTML = '<option>Loading…</option>';

try {
const sources = await window.electronAPI.getSources();
sourcePicker.classList.remove('loading');
sourcePicker.innerHTML = "";

if (!sources.length) {
sourcePicker.innerHTML = "<option>No windows found</option>";
return;
}

for (const source of sources) {
const opt = document.createElement('option');
opt.value = source.id;
opt.textContent = source.name;
sourcePicker.appendChild(opt);
}

sourcePicker.onchange = () => {
const sourceId = sourcePicker.value;
const sourceName = sourcePicker.selectedOptions[0]?.textContent || "Unknown";
recordStatus.textContent = `🔍 Previewing: ${sourceName}`;
startStream(sourceId);
};

sourcePicker.value = sources[0].id;
recordStatus.textContent = `🔍 Previewing: ${sources[0].name}`;
startStream(sources[0].id);
} catch (err) {
console.error("❌ Source error:", err);
sourcePicker.classList.remove('loading');
sourcePicker.innerHTML = "<option>Failed to load sources</option>";
}
}

// 🎥 Start stream
async function startStream(sourceId) {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceId
}
}
});
previewVideo.srcObject = stream;
recordStatus.textContent = "✅ Live preview started";
} catch (err) {
console.error("Stream error:", err);
previewVideo.srcObject = null;
recordStatus.textContent = "❌ Could not start preview";
}
}

// 📈 FPS tracker
function recordFPS() {
const fps = Math.floor(Math.random() * 100);
const time = new Date().toLocaleTimeString();
const color = fps < 30 ? 'rgba(255,0,0,0.8)' : 'rgba(0,0,255,0.8)';

fpsDisplay.textContent = `FPS: ${fps}`;
fpsDisplay.classList.remove('fps-low', 'fps-mid', 'fps-high');
fpsDisplay.classList.add(fps < 30 ? 'fps-low' : fps < 60 ? 'fps-mid' : 'fps-high');

fpsLog.push(fps);
timestamps.push(time);
pointColors.push(color);

if (fpsLog.length > 30) {
fpsLog.shift();
timestamps.shift();
pointColors.shift();
}

chart.update();
}

// 📉 FPS monitor
function monitorFPS() {
monitorInterval = setInterval(() => {
const latest = fpsLog.at(-1);
if (latest > 30 && isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
recordBtn.classList.remove('recording');
sessionTimer.textContent = "";
recordStatus.textContent = "";
isRecording = false;
}
}, 1000);
}

// ▶ Start logging
startBtn.onclick = () => {
if (!intervalID) {
intervalID = setInterval(recordFPS, 1000);
console.log("▶ Started FPS logging");
}
};

// ⏹ Stop logging
stopBtn.onclick = () => {
clearInterval(intervalID);
intervalID = null;
console.log("⏹ Stopped FPS logging");
};

// 📤 Export CSV
exportBtn.onclick = () => {
const csv = fpsLog.map((fps, i) => `${timestamps[i]},${fps}`).join('\n');
const blob = new Blob([`Timestamp,FPS\n${csv}`], { type: 'text/csv' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'fps_log.csv';
link.click();
};

// ⏺ Record toggle
recordBtn.onclick = () => {
if (isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
clearInterval(intervalID);
intervalID = null;
sessionTimer.textContent = "";
recordBtn.classList.remove('recording');
recordStatus.textContent = "";
isRecording = false;
return;
}

if (!previewVideo.srcObject) return;

recordedChunks = [];
mediaRecorder = new MediaRecorder(previewVideo.srcObject, {
mimeType: 'video/webm; codecs=vp9'
});

mediaRecorder.ondataavailable = e => {
if (e.data.size) recordedChunks.push(e.data);
};

mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'low_fps_capture.webm';
link.click();

addSessionEntry({
duration: sessionTimer.textContent || "Unknown",
minFPS: Math.min(...fpsLog),
maxFPS: Math.max(...fpsLog),
exportTime: new Date().toLocaleString(),
windowName: sourcePicker.selectedOptions[0]?.textContent || "Unknown"
});

clearInterval(intervalID); // stop FPS logging
intervalID = null;
};

mediaRecorder.start();
isRecording = true;
recordBtn.classList.add('recording');
recordStatus.textContent = "Recording…";
monitorFPS();

if (!intervalID) {
intervalID = setInterval(recordFPS, 1000);
console.log("▶ Started FPS logging (via Record)");
}

startTime = Date.now();
timerInterval = setInterval(() => {
const secs = Math.floor((Date.now() - startTime) / 1000);
const mins = String(Math.floor(secs / 60)).padStart(2, '0');
const sec = String(secs % 60).padStart(2, '0');
sessionTimer.textContent = `⏱ ${mins}:${sec}`;
}, 1000);

setTimeout(() => {
if (isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
clearInterval(intervalID);
intervalID = null;
sessionTimer.textContent = "";
recordBtn.classList.remove('recording');
recordStatus.textContent = "";
isRecording = false;
console.log("⏹ Auto-stopped after max duration");
}
}, 180000);
};
Top | New | Old
Sandcastler · 26-30, M Best Comment
woah yeah quite a lot wrong here lol

1) timestamps is the entire array, so you need to add the index (in the export section)

2) you're likely getting a NotSupportedError from the mediaRecorder because not all systems support that codec, try

if (!MediaRecorder.isTypeSupported('video/webm; codecs=vp9')) {
console.error('❌ MediaRecorder VP9 not supported');
return;
}

instead

3) You seem to be generating random FPS values? is that correct?

4) chromeMediaSource is deprecated, use navigator.mediaDevices.getDisplayMedia() instead

5) are you aware you're calling clearInterval(intervalID) multiple times?

6) you should perhaps be checking if fpsChart actually exists?

7) what if sessionTimer.textContent is empty? (e.g. if the recording was too short)
PrettyTroubles · 26-30, F
@Sandcastler Wow much thanks!! I have been trying to get my video/game to preview but its not fetching so i was so annoyed XD
im gathering FPS and game footage so that i can see what is causing the fps to dip so low. and im trying to add a record button so i can record my game media and FPS
Northwest · M
@Sandcastler

The exportBtn's click handler has a bug. It incorrectly tries to join the entire timestamps array into every single row of the CSV file.

- Wrong: const csv = fpsLog.map((fps, i) => \timestamps,{fps}`).join('\n');`
- Right: const csv = fpsLog.map((fps, i) => \timestamps,{fps}`).join('\n');`

Placeholder FPS Calculation: The recordFPS function doesn't calculate the actual FPS of the video stream. It generates a random number,. OK for but not for produciton.

Recording Logic: You meant for monitorFPS to stop recording, but it's not properly implemented. You probably meant to capture performance drops (hint: filename: low_fps_capture.webm), it should trigger when FPS < 30.

- [i]Wrong
: if (latest > 30 && isRecording)
- Right: if (latest < 30 && isRecording)

DRY: The logic to stop a recording is repeated in three different places. You can place it in a single helper function.

DDonde · 31-35, M
What's the error you're running into?
PrettyTroubles · 26-30, F
@DDonde I think I fixed it lol it was a syntax error lol my fps chart appears to be working fine now... I hope lol

 
Post Comment