You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, defaultStart = "0", defaultEnd = "10") {
// Create the main container
const container = document.createElement('div');
container.style.fontFamily = 'system-ui, -apple-system, sans-serif';
container.style.padding = '20px';
container.style.maxWidth = '500px';
container.style.margin = '0 auto';
container.style.background = '#ffffff';
container.style.border = '1px solid #e0e0e0';
container.style.borderRadius = '12px';
container.style.boxShadow = '0 4px 15px rgba(0,0,0,0.05)';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.gap = '20px';
container.style.color = '#333';
// Header section wrapper
const header = document.createElement('div');
header.style.display = 'flex';
header.style.alignItems = 'center';
header.style.gap = '15px';
header.style.borderBottom = '1px solid #f0f0f0';
header.style.paddingBottom = '15px';
// Safely incorporate the originalImg as a thumbnail display
if (originalImg) {
try {
const imgCanvas = document.createElement('canvas');
imgCanvas.width = 60;
imgCanvas.height = 60;
const ctx = imgCanvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0, 60, 60);
const imgEl = document.createElement('img');
imgEl.src = imgCanvas.toDataURL('image/png');
imgEl.style.width = '60px';
imgEl.style.height = '60px';
imgEl.style.objectFit = 'cover';
imgEl.style.borderRadius = '8px';
imgEl.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
header.appendChild(imgEl);
} catch(e) {
// Ignore cors issues if any, just skip thumbnail
}
}
const titleContainer = document.createElement('div');
const title = document.createElement('h2');
title.innerText = 'Audio Trimming Tool';
title.style.margin = '0 0 5px 0';
title.style.fontSize = '20px';
const subtitle = document.createElement('p');
subtitle.innerText = 'Trim your audio files directly in the browser.';
subtitle.style.margin = '0';
subtitle.style.fontSize = '12px';
subtitle.style.color = '#666';
titleContainer.appendChild(title);
titleContainer.appendChild(subtitle);
header.appendChild(titleContainer);
container.appendChild(header);
// File Input Section
const fileContainer = document.createElement('div');
fileContainer.style.display = 'flex';
fileContainer.style.flexDirection = 'column';
fileContainer.style.gap = '8px';
const fileLabel = document.createElement('label');
fileLabel.innerText = '1. Select Audio File';
fileLabel.style.fontWeight = '600';
fileLabel.style.fontSize = '14px';
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'audio/*';
fileInput.style.padding = '8px';
fileInput.style.border = '1px dashed #ccc';
fileInput.style.borderRadius = '6px';
fileInput.style.background = '#fafafa';
fileInput.style.cursor = 'pointer';
fileContainer.appendChild(fileLabel);
fileContainer.appendChild(fileInput);
container.appendChild(fileContainer);
// Original Audio Preview
const originalAudioContainer = document.createElement('div');
originalAudioContainer.style.display = 'none';
originalAudioContainer.style.flexDirection = 'column';
originalAudioContainer.style.gap = '8px';
const originalAudioLabel = document.createElement('label');
originalAudioLabel.innerText = 'Original Audio Preview:';
originalAudioLabel.style.fontSize = '13px';
originalAudioLabel.style.color = '#555';
const originalAudio = document.createElement('audio');
originalAudio.controls = true;
originalAudio.style.width = '100%';
originalAudioContainer.appendChild(originalAudioLabel);
originalAudioContainer.appendChild(originalAudio);
container.appendChild(originalAudioContainer);
// Controls Section
const controlsContainer = document.createElement('div');
controlsContainer.style.display = 'flex';
controlsContainer.style.gap = '15px';
controlsContainer.style.alignItems = 'center';
const startWrapper = document.createElement('div');
startWrapper.style.display = 'flex';
startWrapper.style.flexDirection = 'column';
startWrapper.style.gap = '4px';
const startLabel = document.createElement('label');
startLabel.innerText = '2. Start Time (s)';
startLabel.style.fontWeight = '600';
startLabel.style.fontSize = '14px';
const startInput = document.createElement('input');
startInput.type = 'number';
startInput.value = defaultStart;
startInput.min = '0';
startInput.step = '0.1';
startInput.style.padding = '8px';
startInput.style.border = '1px solid #ccc';
startInput.style.borderRadius = '6px';
startInput.style.width = '100px';
startWrapper.appendChild(startLabel);
startWrapper.appendChild(startInput);
const endWrapper = document.createElement('div');
endWrapper.style.display = 'flex';
endWrapper.style.flexDirection = 'column';
endWrapper.style.gap = '4px';
const endLabel = document.createElement('label');
endLabel.innerText = '3. End Time (s)';
endLabel.style.fontWeight = '600';
endLabel.style.fontSize = '14px';
const endInput = document.createElement('input');
endInput.type = 'number';
endInput.value = defaultEnd;
endInput.min = '0';
endInput.step = '0.1';
endInput.style.padding = '8px';
endInput.style.border = '1px solid #ccc';
endInput.style.borderRadius = '6px';
endInput.style.width = '100px';
endWrapper.appendChild(endLabel);
endWrapper.appendChild(endInput);
controlsContainer.appendChild(startWrapper);
controlsContainer.appendChild(endWrapper);
container.appendChild(controlsContainer);
// Action Button
const trimBtn = document.createElement('button');
trimBtn.innerText = 'Trim Audio';
trimBtn.style.padding = '12px 20px';
trimBtn.style.background = '#007BFF';
trimBtn.style.color = '#fff';
trimBtn.style.border = 'none';
trimBtn.style.borderRadius = '6px';
trimBtn.style.fontWeight = 'bold';
trimBtn.style.fontSize = '15px';
trimBtn.style.cursor = 'pointer';
trimBtn.style.transition = 'background 0.2s';
trimBtn.onmouseover = () => { trimBtn.style.background = '#0056b3'; };
trimBtn.onmouseout = () => { trimBtn.style.background = '#007BFF'; };
container.appendChild(trimBtn);
// Status Message
const statusMsg = document.createElement('div');
statusMsg.style.fontSize = '13px';
statusMsg.style.color = '#d9534f';
statusMsg.style.minHeight = '18px';
container.appendChild(statusMsg);
// Output Section
const outputContainer = document.createElement('div');
outputContainer.style.display = 'none';
outputContainer.style.flexDirection = 'column';
outputContainer.style.gap = '15px';
outputContainer.style.marginTop = '10px';
outputContainer.style.paddingTop = '15px';
outputContainer.style.borderTop = '1px solid #f0f0f0';
const outputLabel = document.createElement('label');
outputLabel.innerText = 'Trimmed Result:';
outputLabel.style.fontWeight = '600';
outputLabel.style.fontSize = '14px';
const trimmedAudio = document.createElement('audio');
trimmedAudio.controls = true;
trimmedAudio.style.width = '100%';
const downloadBtn = document.createElement('a');
downloadBtn.innerText = 'Download Trimmed Audio (WAV)';
downloadBtn.style.padding = '10px 15px';
downloadBtn.style.background = '#28a745';
downloadBtn.style.color = '#fff';
downloadBtn.style.textDecoration = 'none';
downloadBtn.style.borderRadius = '6px';
downloadBtn.style.textAlign = 'center';
downloadBtn.style.fontWeight = 'bold';
downloadBtn.style.fontSize = '14px';
downloadBtn.style.cursor = 'pointer';
outputContainer.appendChild(outputLabel);
outputContainer.appendChild(trimmedAudio);
outputContainer.appendChild(downloadBtn);
container.appendChild(outputContainer);
// Event Listeners
let currentInputFile = null;
fileInput.addEventListener('change', () => {
if (fileInput.files.length > 0) {
currentInputFile = fileInput.files[0];
const url = URL.createObjectURL(currentInputFile);
originalAudio.src = url;
originalAudioContainer.style.display = 'flex';
statusMsg.innerText = '';
// Suggest end time based on actual duration when loaded
originalAudio.onloadedmetadata = () => {
if (originalAudio.duration && isFinite(originalAudio.duration)) {
endInput.value = originalAudio.duration.toFixed(2);
}
};
} else {
currentInputFile = null;
originalAudioContainer.style.display = 'none';
}
});
trimBtn.addEventListener('click', async () => {
if (!currentInputFile) {
statusMsg.style.color = '#d9534f';
statusMsg.innerText = 'Please select an audio file first.';
return;
}
const startVal = parseFloat(startInput.value);
const endVal = parseFloat(endInput.value);
if (isNaN(startVal) || isNaN(endVal) || startVal < 0 || startVal >= endVal) {
statusMsg.style.color = '#d9534f';
statusMsg.innerText = 'Invalid start/end times. Ensure Start is less than End.';
return;
}
try {
statusMsg.style.color = '#007BFF';
statusMsg.innerText = 'Processing your audio...';
trimBtn.disabled = true;
trimBtn.style.opacity = '0.7';
// Decode the selected audio file
const arrayBuffer = await currentInputFile.arrayBuffer();
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const originalBuffer = await audioContext.decodeAudioData(arrayBuffer);
const duration = originalBuffer.duration;
let actualStart = startVal;
let actualEnd = endVal;
if (actualEnd > duration) actualEnd = duration;
if (actualStart >= duration) {
throw new Error('Start time is beyond the audio duration.');
}
const sampleRate = originalBuffer.sampleRate;
const channels = originalBuffer.numberOfChannels;
const startOffset = Math.floor(actualStart * sampleRate);
const endOffset = Math.floor(actualEnd * sampleRate);
const frameCount = endOffset - startOffset;
// Create a new AudioBuffer for the trimmed fragment
const newAudioBuffer = audioContext.createBuffer(channels, frameCount, sampleRate);
// Copy data channel by channel
for (let channel = 0; channel < channels; channel++) {
const channelData = originalBuffer.getChannelData(channel);
const newChannelData = newAudioBuffer.getChannelData(channel);
for (let i = 0; i < frameCount; i++) {
newChannelData[i] = channelData[startOffset + i];
}
}
// Convert to WAV Blob
const wavBlob = audioBufferToWav(newAudioBuffer);
const wavUrl = URL.createObjectURL(wavBlob);
// Update UI Outputs
trimmedAudio.src = wavUrl;
// Format filename safely
const originalName = currentInputFile.name.replace(/\.[^/.]+$/, "");
downloadBtn.href = wavUrl;
downloadBtn.download = `${originalName}_trimmed.wav`;
outputContainer.style.display = 'flex';
statusMsg.style.color = '#28a745';
statusMsg.innerText = 'Audio successfully trimmed!';
// Close context to free resources
if(audioContext.state !== 'closed') {
audioContext.close();
}
} catch (err) {
console.error(err);
statusMsg.style.color = '#d9534f';
statusMsg.innerText = err.message || 'Error processing audio. File might be unsupported or corrupted.';
outputContainer.style.display = 'none';
} finally {
trimBtn.disabled = false;
trimBtn.style.opacity = '1';
}
});
/**
* Helper function to encode AudioBuffer to a WAV format Blob
* @param {AudioBuffer} buffer
* @returns {Blob}
*/
function audioBufferToWav(buffer) {
const numChannels = buffer.numberOfChannels;
const sampleRate = buffer.sampleRate;
const format = 1; // PCM
const bitDepth = 16;
let result;
if (numChannels === 2) {
result = interleave(buffer.getChannelData(0), buffer.getChannelData(1));
} else {
result = buffer.getChannelData(0);
}
const dataLength = result.length * (bitDepth / 8);
const bufferArray = new ArrayBuffer(44 + dataLength);
const view = new DataView(bufferArray);
// write string helper
function writeString(view, offset, string) {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
// RIFF header
writeString(view, 0, 'RIFF');
view.setUint32(4, 36 + dataLength, true); // file length - 8
writeString(view, 8, 'WAVE');
// fmt chunk
writeString(view, 12, 'fmt ');
view.setUint32(16, 16, true); // chunk length
view.setUint16(20, format, true); // format
view.setUint16(22, numChannels, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * numChannels * (bitDepth / 8), true); // byte rate
view.setUint16(32, numChannels * (bitDepth / 8), true); // block align
view.setUint16(34, bitDepth, true); // bits per sample
// data chunk
writeString(view, 36, 'data');
view.setUint32(40, dataLength, true);
// Write PCM samples
let offset = 44;
for (let i = 0; i < result.length; i++, offset += 2) {
let sample = Math.max(-1, Math.min(1, result[i]));
// 16-bit signed integer conversion
sample = sample < 0 ? sample * 32768 : sample * 32767;
view.setInt16(offset, sample, true);
}
return new Blob([view], { type: 'audio/wav' });
// Utility strictly used to interleave stereo channels
function interleave(inputL, inputR) {
const length = inputL.length + inputR.length;
const result = new Float32Array(length);
let index = 0;
let inputIndex = 0;
while (index < length) {
result[index++] = inputL[inputIndex];
result[index++] = inputR[inputIndex];
inputIndex++;
}
return result;
}
}
return container;
}
Apply Changes