Onic Computer – Multi Tools Hub
Welcome to the Future of Frontend Tools
A comprehensive suite of 35+ utilities, built with a sleek, futuristic interface. No backend, no libraries, just pure performance.
Your list is saved for this session only. It will reset when you close this tool.
Add
`,
init: () => {
const input = document.getElementById('todo-input');
const addBtn = document.getElementById('add-todo-btn');
const list = document.getElementById('todo-list');
const addTodo = () => {
const taskText = input.value.trim();
if (taskText === '') return;
const li = document.createElement('li');
const span = document.createElement('span');
span.textContent = taskText;
span.onclick = () => li.classList.toggle('completed');
const deleteBtn = document.createElement('button');
deleteBtn.innerHTML = '×';
deleteBtn.onclick = () => li.remove();
li.appendChild(span);
li.appendChild(deleteBtn);
list.appendChild(li);
input.value = '';
input.focus();
};
addBtn.addEventListener('click', addTodo);
input.addEventListener('keypress', e => {
if (e.key === 'Enter') addTodo();
});
}
},
passwordChecker: {
title: 'Password Strength Checker',
icon: ICONS.lock,
getUI: () => `
`,
init: () => {
const input = document.getElementById('password-check-input');
const fill = document.getElementById('password-strength-fill');
const text = document.getElementById('password-strength-text');
input.addEventListener('input', () => {
const pass = input.value;
let score = 0;
if (pass.length > 8) score++;
if (pass.length > 12) score++;
if (/[A-Z]/.test(pass)) score++;
if (/[a-z]/.test(pass)) score++;
if (/[0-9]/.test(pass)) score++;
if (/[^A-Za-z0-9]/.test(pass)) score++;
let strength = "Very Weak";
let color = 'var(--danger-color)';
let width = '15%';
if (score > 2) { strength = "Weak"; width = '35%'; color = 'var(--danger-color)'; }
if (score > 3) { strength = "Medium"; width = '60%'; color = 'var(--warning-color)'; }
if (score > 4) { strength = "Strong"; width = '80%'; color = 'var(--success-color)'; }
if (score > 5) { strength = "Very Strong"; width = '100%'; color = 'var(--primary-accent)'; }
if (pass.length === 0) {
strength = ""; width = '0%';
}
fill.style.width = width;
fill.style.backgroundColor = color;
text.textContent = strength;
});
}
},
// --- NEW CALCULATOR TOOLS ---
calculator: {
title: 'Calculator',
icon: ICONS.calculator,
getUI: () => `
0
AC
⌫
%
÷
7
8
9
×
4
5
6
-
1
2
3
+
0
.
=
`,
init: () => {
const display = document.getElementById('calc-display');
let currentInput = '0';
let operator = null;
let previousInput = null;
document.querySelector('.calc-grid').addEventListener('click', e => {
if (!e.target.matches('.calc-btn')) return;
const key = e.target.dataset.key;
if (/\d/.test(key)) {
currentInput = currentInput === '0' ? key : currentInput + key;
} else if (key === '.') {
if (!currentInput.includes('.')) currentInput += '.';
} else if (key === 'clear') {
currentInput = '0'; operator = null; previousInput = null;
} else if (key === 'backspace') {
currentInput = currentInput.slice(0, -1) || '0';
} else if (key === '=') {
if (operator && previousInput !== null) {
currentInput = String(eval(`${previousInput} ${operator} ${currentInput}`));
operator = null; previousInput = null;
}
} else { // Operator
if (operator && previousInput !== null) {
currentInput = String(eval(`${previousInput} ${operator} ${currentInput}`));
}
operator = key;
previousInput = currentInput;
currentInput = '0';
}
display.textContent = currentInput;
});
}
},
discountCalculator: {
title: 'Discount Calculator',
icon: ICONS.tag,
getUI: () => `
Original Price:
Discount (%):
`,
init: () => {
const priceInput = document.getElementById('dc-price');
const discountInput = document.getElementById('dc-discount');
const output = document.getElementById('dc-output');
function calculate() {
const price = parseFloat(priceInput.value);
const discount = parseFloat(discountInput.value);
if (isNaN(price) || isNaN(discount)) {
output.innerHTML = 'Enter values to see result. ';
return;
}
const saved = price * (discount / 100);
const finalPrice = price - saved;
output.innerHTML = `
You Save: $${saved.toFixed(2)}
Final Price: $${finalPrice.toFixed(2)}
`;
}
[priceInput, discountInput].forEach(el => el.addEventListener('input', calculate));
}
},
tipCalculator: {
title: 'Tip Calculator',
icon: ICONS.calculator,
getUI: () => `
Bill Amount:
Tip Percentage:
Number of People:
`,
init: () => {
const billInput = document.getElementById('tip-bill');
const percentInput = document.getElementById('tip-percent');
const peopleInput = document.getElementById('tip-people');
const output = document.getElementById('tip-output');
function calculate() {
const bill = parseFloat(billInput.value);
const percent = parseFloat(percentInput.value);
const people = parseInt(peopleInput.value, 10);
if (isNaN(bill) || isNaN(percent) || isNaN(people) || people < 1) {
output.innerHTML = 'Enter valid values. ';
return;
}
const totalTip = bill * (percent / 100);
const totalBill = bill + totalTip;
const tipPerPerson = totalTip / people;
const totalPerPerson = totalBill / people;
output.innerHTML = `
Tip per Person: $${tipPerPerson.toFixed(2)}
Total per Person: $${totalPerPerson.toFixed(2)}
Total Tip: $${totalTip.toFixed(2)}
Total Bill: $${totalBill.toFixed(2)}
`;
}
[billInput, percentInput, peopleInput].forEach(el => el.addEventListener('input', calculate));
}
},
// --- NEW DATE & TIME TOOLS ---
dateCalculator: {
title: 'Date Calculator',
icon: ICONS.calendar,
getUI: () => `
Start Date:
End Date:
Calculate Duration
`,
init: () => {
document.getElementById('date-start').valueAsDate = new Date();
document.getElementById('date-end').valueAsDate = new Date();
document.getElementById('date-calc-btn').addEventListener('click', () => {
const start = new Date(document.getElementById('date-start').value);
const end = new Date(document.getElementById('date-end').value);
const output = document.getElementById('date-output');
if (isNaN(start) || isNaN(end)) {
output.innerHTML = 'Please select valid dates.'; return;
}
const diffTime = Math.abs(end - start);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
output.innerHTML = `The difference is ${diffDays} days .`;
});
}
},
randomNumberGenerator: {
title: 'Random Number Generator',
icon: ICONS.dice,
getUI: () => `
Generate
`,
init: () => {
const minInput = document.getElementById('rand-min');
const maxInput = document.getElementById('rand-max');
const output = document.getElementById('rand-output');
const genBtn = document.getElementById('rand-gen-btn');
const generate = () => {
const min = parseInt(minInput.value, 10);
const max = parseInt(maxInput.value, 10);
if (min > max) {
output.textContent = 'Min > Max!'; return;
}
const rand = Math.floor(Math.random() * (max - min + 1)) + min;
output.textContent = rand;
};
genBtn.addEventListener('click', generate);
generate();
}
},
worldTime: {
title: 'World Time',
icon: ICONS.clock,
getUI: () => `
Showing current times in different time zones.
`,
init: () => {
const container = document.getElementById('world-clock-container');
const timeZones = [
{ city: 'New York', zone: 'America/New_York' },
{ city: 'London', zone: 'Europe/London' },
{ city: 'Tokyo', zone: 'Asia/Tokyo' },
{ city: 'Sydney', zone: 'Australia/Sydney' },
{ city: 'Dubai', zone: 'Asia/Dubai' },
{ city: 'Los Angeles', zone: 'America/Los_Angeles' },
];
function updateClocks() {
container.innerHTML = '';
const now = new Date();
timeZones.forEach(tz => {
const time = now.toLocaleTimeString('en-US', { timeZone: tz.zone, hour: '2-digit', minute: '2-digit', second: '2-digit' });
const date = now.toLocaleDateString('en-US', { timeZone: tz.zone, weekday: 'long', month: 'short', day: 'numeric' });
const clockEl = document.createElement('div');
clockEl.className = 'world-clock';
clockEl.innerHTML = `
${tz.city}
${time}
${date}
`;
container.appendChild(clockEl);
});
}
updateClocks();
currentInterval = setInterval(updateClocks, 1000);
}
},
// --- EXISTING TOOLS (No changes below this line, just for context) ---
imageConverter: {
title: 'Image Converter',
getUI: () => `
Upload an image:
Convert to:
PNG
JPEG
WEBP
Convert
`,
init: () => {
const upload = document.getElementById('image-upload');
const convertBtn = document.getElementById('convert-btn');
const formatSelect = document.getElementById('format-select');
const output = document.getElementById('output-container');
let file = null;
upload.addEventListener('change', e => file = e.target.files[0]);
convertBtn.addEventListener('click', () => {
if (!file) {
alert('Please upload an image first.');
return;
}
output.innerHTML = 'Processing...';
const reader = new FileReader();
reader.onload = e => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const format = formatSelect.value;
const ext = format.split('/')[1];
canvas.toBlob(blob => {
const newFilename = `${file.name.split('.')[0]}.${ext}`;
output.innerHTML = '';
output.appendChild(createDownloadLink(blob, newFilename, `Download as ${ext.toUpperCase()}`));
}, format, 0.95);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}
},
imageCompressor: {
title: 'Image Compressor',
getUI: () => `
Upload an image:
Quality (0.1 to 1.0):
0.7
Compress
`,
init: () => {
const upload = document.getElementById('compress-upload');
const compressBtn = document.getElementById('compress-btn');
const qualitySlider = document.getElementById('quality-slider');
const qualityValue = document.getElementById('quality-value');
const output = document.getElementById('compress-output');
let file = null;
upload.addEventListener('change', e => file = e.target.files[0]);
qualitySlider.addEventListener('input', e => qualityValue.textContent = e.target.value);
compressBtn.addEventListener('click', () => {
if (!file) {
alert('Please upload an image first.');
return;
}
output.innerHTML = 'Compressing...';
const reader = new FileReader();
reader.onload = e => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const quality = parseFloat(qualitySlider.value);
canvas.toBlob(blob => {
const originalSize = (file.size / 1024).toFixed(2);
const newSize = (blob.size / 1024).toFixed(2);
output.innerHTML = `Original Size: ${originalSize} KB | New Size: ${newSize} KB `;
output.appendChild(createDownloadLink(blob, `compressed_${file.name}`, 'Download Compressed Image'));
}, 'image/jpeg', quality);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}
},
imageCropper: {
title: 'Image Cropper',
getUI: () => `
Upload an image to crop:
Click and drag on the image to select a crop area.
Crop & Download
`,
init: () => {
const upload = document.getElementById('crop-upload');
const canvas = document.getElementById('crop-canvas');
const ctx = canvas.getContext('2d');
const cropBtn = document.getElementById('crop-btn');
let img, selection = { startX: 0, startY: 0, w: 0, h: 0 }, dragging = false;
upload.addEventListener('change', e => {
const reader = new FileReader();
reader.onload = res => {
img = new Image();
img.onload = () => {
const MAX_WIDTH = 500;
const scale = MAX_WIDTH / img.width;
canvas.width = MAX_WIDTH;
canvas.height = img.height * scale;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
img.src = res.target.result;
};
reader.readAsDataURL(e.target.files[0]);
});
canvas.addEventListener('mousedown', e => {
dragging = true;
selection.startX = e.offsetX;
selection.startY = e.offsetY;
});
canvas.addEventListener('mousemove', e => {
if (!dragging) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
selection.w = e.offsetX - selection.startX;
selection.h = e.offsetY - selection.startY;
ctx.strokeStyle = 'red';
ctx.strokeRect(selection.startX, selection.startY, selection.w, selection.h);
});
canvas.addEventListener('mouseup', () => dragging = false);
cropBtn.addEventListener('click', () => {
if (!img || selection.w === 0 || selection.h === 0) {
alert('Please upload an image and select an area.');
return;
}
const scaleX = img.naturalWidth / canvas.width;
const scaleY = img.naturalHeight / canvas.height;
const cropCanvas = document.createElement('canvas');
cropCanvas.width = Math.abs(selection.w * scaleX);
cropCanvas.height = Math.abs(selection.h * scaleY);
const cropCtx = cropCanvas.getContext('2d');
cropCtx.drawImage(
img,
Math.min(selection.startX, selection.startX + selection.w) * scaleX,
Math.min(selection.startY, selection.startY + selection.h) * scaleY,
Math.abs(selection.w * scaleX),
Math.abs(selection.h * scaleY),
0, 0,
cropCanvas.width, cropCanvas.height
);
cropCanvas.toBlob(blob => {
createDownloadLink(blob, 'cropped-image.png', 'Download Cropped Image').click();
}, 'image/png');
});
}
},
videoConverter: {
title: 'Video Recorder (Webcam)',
getUI: () => `
Note: This tool records video from your webcam. True file conversion is not possible without backend or heavy libraries.
Start Recording
`,
init: () => {
const preview = document.getElementById('video-preview');
const recordBtn = document.getElementById('record-btn');
const output = document.getElementById('video-output');
let mediaRecorder, chunks = [];
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
preview.srcObject = stream;
recordBtn.onclick = () => {
if (!mediaRecorder || mediaRecorder.state === 'inactive') {
mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
mediaRecorder.ondataavailable = e => chunks.push(e.data);
mediaRecorder.onstop = () => {
const blob = new Blob(chunks, { type: 'video/webm' });
chunks = [];
output.innerHTML = '';
output.appendChild(createDownloadLink(blob, 'recording.webm', 'Download Recording'));
};
mediaRecorder.start();
recordBtn.querySelector('span').textContent = 'Stop Recording';
recordBtn.style.borderColor = 'red';
} else {
mediaRecorder.stop();
recordBtn.querySelector('span').textContent = 'Start Recording';
recordBtn.style.borderColor = 'var(--primary-accent)';
}
};
}).catch(err => {
output.textContent = 'Error: Could not access webcam. ' + err.message;
});
}
},
audioConverter: {
title: 'Audio Recorder',
getUI: () => `
Record audio from your microphone.
Start Recording
`,
init: () => {
const recordBtn = document.getElementById('audio-record-btn');
const output = document.getElementById('audio-output');
let mediaRecorder, chunks = [];
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
recordBtn.onclick = () => {
if (!mediaRecorder || mediaRecorder.state === 'inactive') {
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = e => chunks.push(e.data);
mediaRecorder.onstop = () => {
const blob = new Blob(chunks, { type: 'audio/webm' });
chunks = [];
output.innerHTML = '';
output.appendChild(createDownloadLink(blob, 'recording.webm', 'Download Audio'));
};
mediaRecorder.start();
recordBtn.querySelector('span').textContent = 'Stop Recording';
recordBtn.style.borderColor = 'red';
} else {
mediaRecorder.stop();
recordBtn.querySelector('span').textContent = 'Start Recording';
recordBtn.style.borderColor = 'var(--primary-accent)';
}
};
}).catch(err => {
output.textContent = 'Error: Could not access microphone. ' + err.message;
});
}
},
audioTrimmer: {
title: 'Audio Trimmer',
getUI: () => `
Upload an audio file:
Start Time:
End Time:
Trim & Download
`,
init: () => {
const upload = document.getElementById('audio-upload');
const controls = document.getElementById('trim-controls');
const trimBtn = document.getElementById('trim-btn');
const output = document.getElementById('trim-output');
let audioBuffer;
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
upload.addEventListener('change', e => {
const file = e.target.files[0];
if (!file) return;
output.textContent = 'Loading audio...';
const reader = new FileReader();
reader.onload = res => {
audioContext.decodeAudioData(res.target.result, buffer => {
audioBuffer = buffer;
document.getElementById('end-time').value = buffer.duration.toFixed(1);
controls.style.display = 'block';
output.textContent = `Audio loaded. Duration: ${buffer.duration.toFixed(2)}s`;
}, err => output.textContent = 'Error decoding audio file.');
};
reader.readAsArrayBuffer(file);
});
trimBtn.addEventListener('click', () => {
if (!audioBuffer) return;
const startTime = parseFloat(document.getElementById('start-time').value);
const endTime = parseFloat(document.getElementById('end-time').value);
if (startTime >= endTime || endTime > audioBuffer.duration) {
alert('Invalid start or end time.');
return;
}
const startOffset = Math.floor(startTime * audioBuffer.sampleRate);
const endOffset = Math.floor(endTime * audioBuffer.sampleRate);
const frameCount = endOffset - startOffset;
const newBuffer = audioContext.createBuffer(
audioBuffer.numberOfChannels,
frameCount,
audioBuffer.sampleRate
);
for (let i = 0; i < audioBuffer.numberOfChannels; i++) {
const channelData = audioBuffer.getChannelData(i);
const newChannelData = newBuffer.getChannelData(i);
newChannelData.set(channelData.subarray(startOffset, endOffset));
}
const wavBlob = bufferToWave(newBuffer, frameCount);
output.innerHTML = '';
output.appendChild(createDownloadLink(wavBlob, 'trimmed-audio.wav', 'Download Trimmed WAV'));
});
function bufferToWave(abuffer, len) {
let numOfChan = abuffer.numberOfChannels,
length = len * numOfChan * 2 + 44,
buffer = new ArrayBuffer(length),
view = new DataView(buffer),
channels = [], i, sample,
offset = 0,
pos = 0;
setUint32(0x46464952); setUint32(length - 8); setUint32(0x45564157);
setUint32(0x20746d66); setUint32(16); setUint16(1); setUint16(numOfChan);
setUint32(abuffer.sampleRate); setUint32(abuffer.sampleRate * 2 * numOfChan);
setUint16(numOfChan * 2); setUint16(16); setUint32(0x61746164);
setUint32(length - pos - 4);
for (i = 0; i < abuffer.numberOfChannels; i++)
channels.push(abuffer.getChannelData(i));
while (pos < length) {
for (i = 0; i < numOfChan; i++) {
sample = Math.max(-1, Math.min(1, channels[i][offset]));
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0;
view.setInt16(pos, sample, true);
pos += 2;
}
offset++;
}
return new Blob([view], { type: 'audio/wav' });
function setUint16(data) { view.setUint16(pos, data, true); pos += 2; }
function setUint32(data) { view.setUint32(pos, data, true); pos += 4; }
}
}
},
ageCalculator: {
title: 'Age Calculator',
getUI: () => `
Enter your Date of Birth:
Calculate Age
`,
init: () => {
document.getElementById('calc-age-btn').addEventListener('click', () => {
const dobString = document.getElementById('dob-input').value;
const resultDiv = document.getElementById('age-result');
if (!dobString) {
resultDiv.textContent = 'Please select a date.';
return;
}
const dob = new Date(dobString);
const today = new Date();
let age = today.getFullYear() - dob.getFullYear();
const m = today.getMonth() - dob.getMonth();
if (m < 0 || (m === 0 && today.getDate() < dob.getDate())) {
age--;
}
resultDiv.textContent = `You are ${age} years old.`;
});
}
},
emiCalculator: {
title: 'EMI Calculator',
getUI: () => `
Loan Amount ($):
Annual Interest Rate (%):
Loan Tenure (Years):
Calculate EMI
`,
init: () => {
document.getElementById('calc-emi-btn').addEventListener('click', () => {
const p = parseFloat(document.getElementById('principal').value);
const r = parseFloat(document.getElementById('interest').value) / 12 / 100;
const n = parseFloat(document.getElementById('tenure').value) * 12;
const resultDiv = document.getElementById('emi-result');
if (isNaN(p) || isNaN(r) || isNaN(n) || p <= 0 || r <= 0 || n <= 0) {
resultDiv.textContent = 'Please enter valid positive numbers in all fields.';
return;
}
const emi = p * r * (Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
const totalPayable = emi * n;
const totalInterest = totalPayable - p;
resultDiv.innerHTML = `
Monthly EMI: $${emi.toFixed(2)}
Total Interest Payable: $${totalInterest.toFixed(2)}
Total Payment (Principal + Interest): $${totalPayable.toFixed(2)}
`;
});
}
},
sipCalculator: {
title: 'SIP Calculator',
getUI: () => `
Monthly Investment ($):
Expected Annual Return Rate (%):
Investment Period (Years):
Calculate
`,
init: () => {
document.getElementById('calc-sip-btn').addEventListener('click', () => {
const i = parseFloat(document.getElementById('monthly-investment').value);
const r = parseFloat(document.getElementById('expected-return').value) / 100 / 12;
const n = parseFloat(document.getElementById('investment-period').value) * 12;
const resultDiv = document.getElementById('sip-result');
if (isNaN(i) || isNaN(r) || isNaN(n) || i <= 0 || r < 0 || n <= 0) {
resultDiv.textContent = 'Please enter valid positive numbers.';
return;
}
const futureValue = i * ((Math.pow(1 + r, n) - 1) / r) * (1 + r);
const investedAmount = i * n;
const wealthGained = futureValue - investedAmount;
resultDiv.innerHTML = `
Invested Amount: $${investedAmount.toFixed(2)}
Est. Returns: $${wealthGained.toFixed(2)}
Total Value: $${futureValue.toFixed(2)}
`;
});
}
},
qrCodeGenerator: {
title: 'QR Code Generator',
getUI: () => `
Enter text or URL:
Your QR Code will appear here
`,
init: () => {
const textInput = document.getElementById('qr-text');
const qrContainer = document.getElementById('qr-code-container');
let qrcode = null;
const generateQR = () => {
const text = textInput.value.trim();
qrContainer.innerHTML = '';
if (text) {
if (!qrcode) {
qrcode = new QRCode(qrContainer, {
text: text,
width: 256,
height: 256,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
} else {
qrcode.makeCode(text);
}
} else {
qrContainer.innerHTML = 'Your QR Code will appear here ';
}
};
textInput.addEventListener('input', generateQR);
generateQR();
}
},
passwordGenerator: {
title: 'Password Generator',
getUI: () => `
Password Length:
16
Uppercase
Lowercase
Numbers
Symbols
Generate Password
`,
init: () => {
const lengthSlider = document.getElementById('pass-length');
const lengthVal = document.getElementById('length-val');
const genBtn = document.getElementById('gen-pass-btn');
const outputDiv = document.getElementById('pass-output');
const inclUpper = document.getElementById('incl-upper');
const inclLower = document.getElementById('incl-lower');
const inclNums = document.getElementById('incl-nums');
const inclSyms = document.getElementById('incl-syms');
const chars = {
upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
lower: 'abcdefghijklmnopqrstuvwxyz',
nums: '0123456789',
syms: '!@#$%^&*()_+~`|}{[]:;?><,./-='
};
lengthSlider.addEventListener('input', () => lengthVal.textContent = lengthSlider.value);
const generatePassword = () => {
const length = parseInt(lengthSlider.value);
let charset = '';
if (inclUpper.checked) charset += chars.upper;
if (inclLower.checked) charset += chars.lower;
if (inclNums.checked) charset += chars.nums;
if (inclSyms.checked) charset += chars.syms;
if (charset === '') {
outputDiv.textContent = 'Please select at least one character set.';
return;
}
let password = '';
for (let i = 0; i < length; i++) {
password += charset.charAt(Math.floor(Math.random() * charset.length));
}
outputDiv.textContent = password;
};
genBtn.addEventListener('click', generatePassword);
outputDiv.addEventListener('click', () => {
if (outputDiv.textContent) {
navigator.clipboard.writeText(outputDiv.textContent)
.then(() => alert('Password copied to clipboard!'))
.catch(err => alert('Failed to copy password.'));
}
});
generatePassword();
}
},
loremIpsumGenerator: {
title: 'Lorem Ipsum Generator',
getUI: () => `
Number of Paragraphs:
Generate
Generated text will appear here.
`,
init: () => {
const generateBtn = document.getElementById('lorem-generate');
const outputDiv = document.getElementById('lorem-output');
const loremText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Phasellus egestas tellus rutrum tellus pellentesque eu. Mattis enim ut tellus elementum sagittis vitae et. Congue nisi vitae suscipit tellus mauris a diam. Ac turpis egestas integer eget aliquet nibh praesent. Nisl tincidunt eget nullam non.";
const generateLorem = () => {
const count = parseInt(document.getElementById('lorem-count').value, 10);
let result = '';
if (count > 0) {
for (let i = 0; i < count; i++) {
result += `${loremText}
`;
}
outputDiv.innerHTML = result;
} else {
outputDiv.innerHTML = 'Please enter a valid number of paragraphs. ';
}
};
generateBtn.addEventListener('click', generateLorem);
generateLorem();
}
},
wordCounter: {
title: 'Word, Character & Sentence Counter',
getUI: () => `
`,
init: () => {
document.getElementById('text-input').addEventListener('input', e => {
const text = e.target.value;
const words = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
const chars = text.length;
const sentences = (text.match(/[.!?…]+/g) || []).length;
document.getElementById('count-result').innerHTML = `
Words: ${words}
Characters: ${chars}
Sentences: ${sentences}
`;
});
}
},
base64EncoderDecoder: {
title: 'Base64 Encoder/Decoder',
getUI: () => `
Input Text:
Encode
Decode
`,
init: () => {
const input = document.getElementById('base64-input');
const output = document.getElementById('base64-output');
document.getElementById('encode-btn').addEventListener('click', () => {
try {
output.textContent = btoa(unescape(encodeURIComponent(input.value)));
} catch (e) {
output.textContent = 'Error: ' + e.message;
}
});
document.getElementById('decode-btn').addEventListener('click', () => {
try {
output.textContent = decodeURIComponent(escape(atob(input.value)));
} catch (e) {
output.textContent = 'Error: Invalid Base64 string.';
}
});
}
},
caseConverter: {
title: 'Case Converter',
getUI: () => `
Your Text:
UPPERCASE
lowercase
Title Case
Sentence case
`,
init: () => {
const textarea = document.getElementById('case-input');
document.getElementById('case-upper').addEventListener('click', () => {
textarea.value = textarea.value.toUpperCase();
});
document.getElementById('case-lower').addEventListener('click', () => {
textarea.value = textarea.value.toLowerCase();
});
document.getElementById('case-title').addEventListener('click', () => {
textarea.value = textarea.value.toLowerCase().replace(/\b\w/g, char => char.toUpperCase());
});
document.getElementById('case-sentence').addEventListener('click', () => {
textarea.value = textarea.value.toLowerCase().replace(/(^\w{1}|\.\s*\w{1})/g, char => char.toUpperCase());
});
}
},
colorPicker: {
title: 'Color Picker',
getUI: () => `
Select a color:
`,
init: () => {
const colorInput = document.getElementById('color-input');
const output = document.getElementById('color-output');
const updateColorValues = (hex) => {
const rgb = hexToRgb(hex);
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
output.innerHTML = `
HEX: ${hex}
RGB: rgb(${rgb.r}, ${rgb.g}, ${rgb.b})
HSL: hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)
`;
output.style.borderColor = hex;
};
colorInput.addEventListener('input', e => updateColorValues(e.target.value));
const initialColor = document.body.classList.contains('light-mode') ? '#007bff' : '#42f8f5';
colorInput.value = initialColor;
updateColorValues(initialColor);
function hexToRgb(hex) {
let r = 0, g = 0, b = 0;
if (hex.length == 4) { r = "0x" + hex[1] + hex[1]; g = "0x" + hex[2] + hex[2]; b = "0x" + hex[3] + hex[3]; }
else if (hex.length == 7) { r = "0x" + hex[1] + hex[2]; g = "0x" + hex[3] + hex[4]; b = "0x" + hex[5] + hex[6]; }
return { r: +r, g: +g, b: +b };
}
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let h=0, s, l = (max + min) / 2;
if (max != min) {
let d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
} else { h = s = 0; }
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
}
}
},
textToSpeech: {
title: 'Text to Speech',
getUI: () => `
Text to speak:
Speak
`,
init: () => {
const speakBtn = document.getElementById('speak-btn');
const textInput = document.getElementById('tts-text');
speakBtn.addEventListener('click', () => {
const text = textInput.value;
if (speechSynthesis.speaking) { speechSynthesis.cancel(); }
if (text !== '') {
const utterance = new SpeechSynthesisUtterance(text);
speechSynthesis.speak(utterance);
}
});
}
},
speechToText: {
title: 'Speech to Text',
getUI: () => `
Click the button and start speaking.
Start Listening
Transcript will appear here...
`,
init: () => {
const sttBtn = document.getElementById('stt-btn');
const output = document.getElementById('stt-output');
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
output.innerHTML = 'Speech recognition is not supported in your browser.';
sttBtn.disabled = true;
return;
}
const recognition = new SpeechRecognition();
recognition.interimResults = true;
recognition.lang = 'en-US';
let listening = false;
recognition.onresult = event => {
let transcript = Array.from(event.results).map(result => result[0]).map(result => result.transcript).join('');
output.textContent = transcript;
};
recognition.onend = () => {
sttBtn.querySelector('span').textContent = 'Start Listening';
listening = false;
};
sttBtn.addEventListener('click', () => {
if (listening) { recognition.stop(); }
else { recognition.start(); sttBtn.querySelector('span').textContent = 'Stop Listening'; }
listening = !listening;
});
}
},
jsonFormatter: {
title: 'JSON Formatter & Validator',
getUI: () => `
Paste your JSON here:
Format JSON
`,
init: () => {
document.getElementById('format-json-btn').addEventListener('click', () => {
const input = document.getElementById('json-input').value;
const output = document.getElementById('json-output');
try {
const parsed = JSON.parse(input);
const formatted = JSON.stringify(parsed, null, 4);
output.innerHTML = `${formatted} `;
output.style.borderColor = 'var(--primary-accent)';
} catch (e) {
output.textContent = 'Invalid JSON: ' + e.message;
output.style.borderColor = 'red';
}
});
}
},
urlEncoderDecoder: {
title: 'URL Encoder / Decoder',
getUI: () => `
Input Text or URL Component:
Encode
Decode
`,
init: () => {
const input = document.getElementById('url-input');
const output = document.getElementById('url-output');
document.getElementById('url-encode-btn').addEventListener('click', () => {
try { output.textContent = encodeURIComponent(input.value); }
catch (e) { output.textContent = 'Error: ' + e.message; }
});
document.getElementById('url-decode-btn').addEventListener('click', () => {
try { output.textContent = decodeURIComponent(input.value); }
catch (e) { output.textContent = 'Error: Invalid URL component string.'; }
});
}
},
unitConverter: {
title: 'Unit Converter',
getUI: () => `
Category:
Length
Weight
Temperature
=
`,
init: () => {
const categorySelect = document.getElementById('unit-category');
const fromSelect = document.getElementById('from-unit');
const toSelect = document.getElementById('to-unit');
const input = document.getElementById('unit-input');
const output = document.getElementById('unit-output');
const units = {
length: { 'meters': 1, 'kilometers': 1000, 'miles': 1609.34, 'feet': 0.3048 },
weight: { 'grams': 1, 'kilograms': 1000, 'pounds': 453.592, 'ounces': 28.3495 },
temperature: { 'celsius': 'c', 'fahrenheit': 'f', 'kelvin': 'k' }
};
function populateUnits() {
const category = categorySelect.value;
fromSelect.innerHTML = ''; toSelect.innerHTML = '';
Object.keys(units[category]).forEach(unit => {
fromSelect.add(new Option(unit, unit));
toSelect.add(new Option(unit, unit));
});
if (category === 'length') { fromSelect.value = 'meters'; toSelect.value = 'kilometers'; }
else if (category === 'weight') { fromSelect.value = 'kilograms'; toSelect.value = 'grams'; }
else { fromSelect.value = 'celsius'; toSelect.value = 'fahrenheit'; }
convert();
}
function convert() {
const category = categorySelect.value;
const fromUnit = fromSelect.value;
const toUnit = toSelect.value;
const val = parseFloat(input.value);
if (isNaN(val)) return;
let result;
if (category !== 'temperature') {
const baseValue = val * units[category][fromUnit];
result = baseValue / units[category][toUnit];
} else {
if (fromUnit === toUnit) { result = val; }
else if (fromUnit === 'celsius') { result = toUnit === 'fahrenheit' ? (val * 9/5) + 32 : val + 273.15; }
else if (fromUnit === 'fahrenheit') { result = toUnit === 'celsius' ? (val - 32) * 5/9 : ((val - 32) * 5/9) + 273.15; }
else { result = toUnit === 'celsius' ? val - 273.15 : ((val - 273.15) * 9/5) + 32; }
}
output.value = result.toFixed(3);
}
categorySelect.addEventListener('change', populateUnits);
[fromSelect, toSelect, input].forEach(el => el.addEventListener('change', convert));
input.addEventListener('input', convert);
populateUnits();
}
},
bmiCalculator: {
title: 'BMI Calculator',
getUI: () => `
Height (cm):
Weight (kg):
Calculate BMI
`,
init: () => {
document.getElementById('calc-bmi-btn').addEventListener('click', () => {
const height = parseFloat(document.getElementById('height').value) / 100;
const weight = parseFloat(document.getElementById('weight').value);
const resultDiv = document.getElementById('bmi-result');
if (isNaN(height) || isNaN(weight) || height <= 0 || weight <= 0) {
resultDiv.textContent = 'Please enter valid height and weight.';
return;
}
const bmi = weight / (height * height);
let category;
if (bmi < 18.5) category = 'Underweight';
else if (bmi < 24.9) category = 'Normal weight';
else if (bmi < 29.9) category = 'Overweight';
else category = 'Obesity';
resultDiv.innerHTML = `Your BMI is ${bmi.toFixed(2)} . This is considered: ${category} .`;
});
}
},
timerStopwatch: {
title: 'Timer / Stopwatch',
getUI: () => `
Stopwatch
Timer
Stopwatch
00:00:00.000
Start
Stop
Reset
`,
init: () => {
const stopwatchView = document.getElementById('stopwatch-view');
const timerView = document.getElementById('timer-view');
document.getElementById('show-stopwatch').addEventListener('click', () => {
stopwatchView.style.display = 'block'; timerView.style.display = 'none';
});
document.getElementById('show-timer').addEventListener('click', () => {
stopwatchView.style.display = 'none'; timerView.style.display = 'block';
});
let swInterval, swStartTime, swElapsedTime = 0;
const swDisplay = document.getElementById('stopwatch-display');
function formatTime(ms) {
const d = new Date(ms);
return `${d.getUTCMinutes().toString().padStart(2, '0')}:${d.getUTCSeconds().toString().padStart(2, '0')}.${d.getUTCMilliseconds().toString().padStart(3, '0')}`;
}
document.getElementById('sw-start').addEventListener('click', () => {
if (swInterval) return;
swStartTime = Date.now() - swElapsedTime;
swInterval = setInterval(() => {
swElapsedTime = Date.now() - swStartTime;
swDisplay.textContent = formatTime(swElapsedTime);
}, 10);
});
document.getElementById('sw-stop').addEventListener('click', () => { clearInterval(swInterval); swInterval = null; });
document.getElementById('sw-reset').addEventListener('click', () => {
clearInterval(swInterval); swInterval = null; swElapsedTime = 0;
swDisplay.textContent = '00:00:00.000';
});
let timerInterval, totalSeconds;
const h_in=document.getElementById('timer-h'), m_in=document.getElementById('timer-m'), s_in=document.getElementById('timer-s');
function startTimer() {
if (timerInterval) return;
timerInterval = setInterval(() => {
if (totalSeconds <= 0) {
clearInterval(timerInterval); timerInterval = null;
alert('Timer finished!');
updateTimerDisplay(); return;
}
totalSeconds--;
updateTimerDisplay();
}, 1000);
}
function updateTimerDisplay() {
h_in.value = Math.floor(totalSeconds / 3600);
m_in.value = Math.floor((totalSeconds % 3600) / 60);
s_in.value = totalSeconds % 60;
}
document.getElementById('timer-start').addEventListener('click', () => {
if(!timerInterval) { totalSeconds = (parseInt(h_in.value)*3600) + (parseInt(m_in.value)*60) + parseInt(s_in.value); }
startTimer();
});
document.getElementById('timer-pause').addEventListener('click', () => { clearInterval(timerInterval); timerInterval = null; });
document.getElementById('timer-reset').addEventListener('click', () => {
clearInterval(timerInterval); timerInterval = null;
h_in.value=0; m_in.value=1; s_in.value=30;
totalSeconds = 90;
updateTimerDisplay();
});
}
},
about: {
title: 'About Onic Computer',
getUI: () => `
Onic Computer – Multi Tools Hub is a demonstration of what's possible with modern, vanilla web technologies.
This entire website is built using only HTML, CSS, and JavaScript . There are no external frameworks like React or Vue, and no UI libraries like Bootstrap, with only minimal, targeted libraries for complex tasks like PDF manipulation.
Key Features:
Pure Frontend: All 35+ tools run directly in your browser.
Light/Dark Mode: A beautiful, persistent theme toggle for user comfort.
High Performance: No heavy frameworks means a faster, more responsive experience.
Modern UI/UX: A futuristic, glowing design built with CSS variables, transitions, and shadows.
Responsive Design: Fully functional on desktop, tablet, and mobile devices.
This project was created to showcase proficiency in core web fundamentals and the power of native browser APIs like Web Audio, Web Speech, and Canvas.
`,
init:() => {}
},
};
});
Post Views: 2