You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, defaultAudioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', visualizerColor = '#4CAF50') {
// 1. Create a wrapper container (The "Video Player")
const container = document.createElement('div');
container.style.fontFamily = 'Arial, sans-serif';
container.style.width = '100%';
container.style.maxWidth = '800px';
container.style.margin = '0 auto';
container.style.background = '#1a1a1a';
container.style.borderRadius = '10px';
container.style.overflow = 'hidden';
container.style.boxShadow = '0 8px 30px rgba(0,0,0,0.6)';
container.style.display = 'flex';
container.style.flexDirection = 'column';
// 2. Header
const header = document.createElement('div');
header.style.padding = '15px';
header.style.background = '#000';
header.style.color = '#fff';
header.style.fontWeight = 'bold';
header.style.textAlign = 'center';
header.style.letterSpacing = '1px';
header.innerText = 'MP3 to MP4 Converter / Video Player';
container.appendChild(header);
// 3. Canvas (The Video Screen)
const canvas = document.createElement('canvas');
canvas.width = originalImg.width || 800;
canvas.height = originalImg.height || 600;
canvas.style.width = '100%';
canvas.style.display = 'block';
canvas.style.maxHeight = '60vh';
canvas.style.objectFit = 'contain';
canvas.style.backgroundColor = '#000';
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
// Utility: Draw Image (Background) maintaining max coverage, plus a sleek dark overlay
function drawBackground() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const canvasRatio = canvas.width / canvas.height;
const imgRatio = originalImg.width / originalImg.height;
let drawWidth = canvas.width;
let drawHeight = canvas.height;
let offsetX = 0;
let offsetY = 0;
if (imgRatio > canvasRatio) {
drawWidth = canvas.height * imgRatio;
offsetX = (canvas.width - drawWidth) / 2;
} else {
drawHeight = canvas.width / imgRatio;
offsetY = (canvas.height - drawHeight) / 2;
}
ctx.drawImage(originalImg, offsetX, offsetY, drawWidth, drawHeight);
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; // Darken to make visualizer pop
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
if (originalImg.complete && originalImg.naturalHeight !== 0) {
drawBackground();
} else {
originalImg.onload = drawBackground;
}
// 4. Audio Engine
const audio = new Audio();
audio.crossOrigin = 'anonymous'; // Important for Web Audio API if URL is external
audio.src = defaultAudioUrl;
let audioCtx, analyser, source, dataArray, bufferLength;
let isPlaying = false;
let animationId;
let audioInitialized = false;
// 5. Controls UI
const controls = document.createElement('div');
controls.style.display = 'flex';
controls.style.alignItems = 'center';
controls.style.flexWrap = 'wrap';
controls.style.padding = '15px';
controls.style.background = '#222';
controls.style.gap = '15px';
container.appendChild(controls);
const btnStyle = `
padding: 8px 16px;
border: none;
border-radius: 5px;
background: #4CAF50;
color: #fff;
cursor: pointer;
font-weight: bold;
transition: background 0.2s;
`;
const playBtn = document.createElement('button');
playBtn.innerHTML = '▶ Play';
playBtn.style.cssText = btnStyle;
controls.appendChild(playBtn);
const progressContainer = document.createElement('div');
progressContainer.style.flex = '1 1 150px';
progressContainer.style.height = '8px';
progressContainer.style.background = '#444';
progressContainer.style.borderRadius = '4px';
progressContainer.style.cursor = 'pointer';
progressContainer.style.position = 'relative';
const progressBar = document.createElement('div');
progressBar.style.height = '100%';
progressBar.style.width = '0%';
progressBar.style.background = visualizerColor;
progressBar.style.borderRadius = '4px';
progressBar.style.pointerEvents = 'none';
progressContainer.appendChild(progressBar);
controls.appendChild(progressContainer);
const timeDisplay = document.createElement('div');
timeDisplay.style.color = '#ddd';
timeDisplay.style.fontSize = '14px';
timeDisplay.style.fontFamily = 'monospace';
timeDisplay.innerText = '0:00 / 0:00';
controls.appendChild(timeDisplay);
const recordBtn = document.createElement('button');
recordBtn.innerHTML = '📹 Record MP4';
recordBtn.style.cssText = btnStyle;
recordBtn.style.background = '#E91E63';
recordBtn.title = 'Convert current Audio and Visuals to a downloadable Video File';
controls.appendChild(recordBtn);
// 6. Functions for Control Handlers
const formatTime = (time) => {
if (isNaN(time) || !isFinite(time)) return '0:00';
const mins = Math.floor(time / 60);
const secs = Math.floor(time % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
};
audio.addEventListener('timeupdate', () => {
const percent = (audio.currentTime / audio.duration) * 100 || 0;
progressBar.style.width = `${percent}%`;
timeDisplay.innerText = `${formatTime(audio.currentTime)} / ${formatTime(audio.duration)}`;
});
progressContainer.addEventListener('click', (e) => {
const rect = progressContainer.getBoundingClientRect();
const pos = (e.clientX - rect.left) / rect.width;
if(audio.duration) {
audio.currentTime = pos * audio.duration;
}
});
// 7. Web Audio API Init for Canvas Visualizer
function initAudio() {
if (audioInitialized) return;
const AudioContext = window.AudioContext || window.webkitAudioContext;
audioCtx = new AudioContext();
analyser = audioCtx.createAnalyser();
analyser.fftSize = 256;
source = audioCtx.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(audioCtx.destination);
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
audioInitialized = true;
}
// 8. Visualizer Rendering Loop
function drawVisualizer() {
drawBackground(); // Base image
if (audioInitialized) {
analyser.getByteFrequencyData(dataArray);
const width = canvas.width;
const height = canvas.height;
const barWidth = (width / bufferLength) * 2.2;
let barHeight;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i] * (height / 350);
ctx.fillStyle = visualizerColor;
// Add an alpha fade based on spectrum positioning
ctx.globalAlpha = Math.max(0.2, 0.9 - (i / bufferLength) * 0.5);
ctx.fillRect(x, height - barHeight, barWidth, barHeight);
ctx.globalAlpha = 1.0;
x += barWidth + 1;
}
}
animationId = requestAnimationFrame(drawVisualizer);
}
function togglePlay() {
if (!audioInitialized) initAudio();
if (audioCtx && audioCtx.state === 'suspended') {
audioCtx.resume();
}
if (audio.paused) {
audio.play();
isPlaying = true;
playBtn.innerHTML = '⏸ Pause';
drawVisualizer();
} else {
audio.pause();
isPlaying = false;
playBtn.innerHTML = '▶ Play';
cancelAnimationFrame(animationId);
drawBackground();
}
}
playBtn.addEventListener('click', togglePlay);
audio.addEventListener('ended', () => {
isPlaying = false;
playBtn.innerHTML = '▶ Play';
cancelAnimationFrame(animationId);
drawBackground();
if (isRecording) stopRecording();
});
// 9. Converter API (MediaRecorder captures Canvas + Audio)
let mediaRecorder;
let recordedChunks = [];
let isRecording = false;
recordBtn.addEventListener('click', () => {
if (isRecording) {
stopRecording();
} else {
startRecording();
}
});
function startRecording() {
if (!audioInitialized) initAudio();
try {
// Capture Video Track from Canvas
const stream = canvas.captureStream(30); // 30 FPS
// Mix Audio Track dynamically
const dest = audioCtx.createMediaStreamDestination();
analyser.connect(dest);
const audioTracks = dest.stream.getAudioTracks();
if (audioTracks.length > 0) {
stream.addTrack(audioTracks[0]);
}
// Negotiate MIME support
let mimeType = '';
if (MediaRecorder.isTypeSupported('video/mp4')) {
mimeType = 'video/mp4';
} else if (MediaRecorder.isTypeSupported('video/webm; codecs=vp9,opus')) {
mimeType = 'video/webm; codecs=vp9,opus';
} else if (MediaRecorder.isTypeSupported('video/webm; codecs=vp8,opus')) {
mimeType = 'video/webm; codecs=vp8,opus';
} else {
mimeType = 'video/webm';
}
mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
mediaRecorder.ondataavailable = (e) => {
if (e.data.size > 0) recordedChunks.push(e.data);
};
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: mimeType || 'video/webm' });
recordedChunks = [];
const ext = (mimeType && mimeType.includes('mp4')) ? 'mp4' : 'webm';
const url = URL.createObjectURL(blob);
// Automatically prompt user to download file
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `converted_video.${ext}`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 200);
recordBtn.innerHTML = '📹 Record MP4';
recordBtn.style.background = '#E91E63';
isRecording = false;
};
mediaRecorder.start();
isRecording = true;
recordBtn.innerHTML = '⏹ Stop Processing';
recordBtn.style.background = '#f44336';
if (audio.paused) togglePlay();
} catch (e) {
alert("Error initializing screen recording. Your browser may not fully support Canvas web conversions.");
console.error(e);
}
}
function stopRecording() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
if (!audio.paused) togglePlay();
}
// 10. Source Load Component
const inputContainer = document.createElement('div');
inputContainer.style.display = 'flex';
inputContainer.style.flexWrap = 'wrap';
inputContainer.style.padding = '15px';
inputContainer.style.background = '#111';
inputContainer.style.gap = '10px';
container.appendChild(inputContainer);
const urlInput = document.createElement('input');
urlInput.type = 'text';
urlInput.value = defaultAudioUrl;
urlInput.placeholder = 'Enter MP3 URL or Use Local Audio';
urlInput.style.flex = '1 1 200px';
urlInput.style.padding = '8px 12px';
urlInput.style.border = '1px solid #444';
urlInput.style.borderRadius = '5px';
urlInput.style.background = '#222';
urlInput.style.color = '#fff';
urlInput.style.outline = 'none';
const loadBtn = document.createElement('button');
loadBtn.innerText = 'Load URL';
loadBtn.style.cssText = btnStyle;
loadBtn.style.background = '#FF9800';
const fileLabel = document.createElement('label');
fileLabel.innerText = '📁 Local MP3';
fileLabel.style.cssText = btnStyle;
fileLabel.style.background = '#00BCD4';
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'audio/*';
fileInput.style.display = 'none';
const applyAudio = (srcStr) => {
if (isRecording) stopRecording();
audio.src = srcStr;
audio.load();
if (isPlaying) {
togglePlay();
} else {
drawBackground();
}
progressBar.style.width = '0%';
timeDisplay.innerText = '0:00 / 0:00';
};
loadBtn.addEventListener('click', () => {
if (urlInput.value) applyAudio(urlInput.value);
});
fileInput.addEventListener('change', (e) => {
if(e.target.files.length > 0) {
const file = e.target.files[0];
const objectUrl = URL.createObjectURL(file);
urlInput.value = file.name;
applyAudio(objectUrl);
}
});
fileLabel.appendChild(fileInput);
inputContainer.appendChild(urlInput);
inputContainer.appendChild(loadBtn);
inputContainer.appendChild(fileLabel);
return container;
}
Apply Changes