Project HTML CSS JavaScript Speed Test Internet

Project HTML CSS JavaScript Speed Test Internet

Project HTML CSS JavaScript: Speed Test Internet

Membuat speed test internet menggunakan HTML, CSS, dan JavaScript adalah proyek menarik yang menggabungkan konsep frontend dengan pengukuran performa jaringan. Dalam panduan ini, kita akan membuat alat pengukur kecepatan internet dengan:

✅ Tampilan modern dan responsif
✅ Pengukuran kecepatan download/upload
✅ Animasi visual saat pengukuran
✅ Histori hasil test
✅ Kompatibilitas multi-browser

Struktur Proyek

/internet-speed-test
│── index.html
│── style.css
│── script.js
└── assets/
    ├── images/
    └── fonts/

1. HTML (index.html)

html
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Internet Speed Test - Azroi</title>
    <link rel="stylesheet" href="style.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
    <div class="container">
        <header>
            <h1><i class="fas fa-tachometer-alt"></i> Internet Speed Test</h1>
            <p>Ukur kecepatan unduh dan unggah koneksi internet Anda</p>
        </header>

        <main>
            <div class="speed-test-container">
                <div class="gauge-container">
                    <div class="gauge">
                        <div class="gauge-body">
                            <div class="gauge-fill" id="gaugeFill"></div>
                        </div>
                        <div class="gauge-cover" id="speedValue">0 Mbps</div>
                    </div>
                    <div class="gauge-labels">
                        <span>0</span>
                        <span>50</span>
                        <span>100</span>
                        <span>150</span>
                        <span>200+</span>
                    </div>
                </div>

                <div class="test-info">
                    <div class="info-box" id="downloadInfo">
                        <i class="fas fa-download"></i>
                        <h3>Download</h3>
                        <p class="speed">-</p>
                        <p class="status">Klik mulai untuk test</p>
                    </div>
                    <div class="info-box" id="uploadInfo">
                        <i class="fas fa-upload"></i>
                        <h3>Upload</h3>
                        <p class="speed">-</p>
                        <p class="status">Menunggu...</p>
                    </div>
                    <div class="info-box" id="pingInfo">
                        <i class="fas fa-signal"></i>
                        <h3>Ping</h3>
                        <p class="speed">-</p>
                        <p class="status">-</p>
                    </div>
                </div>

                <button id="startTestBtn" class="test-btn">
                    <i class="fas fa-play"></i> Mulai Test
                </button>
            </div>

            <div class="history-container">
                <h2><i class="fas fa-history"></i> Histori Test</h2>
                <table id="historyTable">
                    <thead>
                        <tr>
                            <th>Tanggal</th>
                            <th>Download</th>
                            <th>Upload</th>
                            <th>Ping</th>
                        </tr>
                    </thead>
                    <tbody>
                        <!-- Data akan diisi oleh JavaScript -->
                    </tbody>
                </table>
            </div>
        </main>

        <footer>
            <p>© 2025 Internet Speed Test | Azroi id</p>
        </footer>
    </div>

    <script src="script.js"></script>
</body>
</html>

2. CSS (style.css)

css
/* Reset & Base Styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

body {
    background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
    color: #333;
    min-height: 100vh;
    padding: 20px;
}

.container {
    max-width: 1000px;
    margin: 0 auto;
    background-color: white;
    border-radius: 15px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
    overflow: hidden;
}

header {
    text-align: center;
    padding: 30px 20px;
    background: linear-gradient(135deg, #4b6cb7 0%, #182848 100%);
    color: white;
}

header h1 {
    font-size: 2.5rem;
    margin-bottom: 10px;
}

header p {
    opacity: 0.9;
}

/* Speed Test Container */
.speed-test-container {
    padding: 30px;
    text-align: center;
}

.gauge-container {
    margin: 0 auto 30px;
    max-width: 300px;
}

.gauge {
    position: relative;
    width: 100%;
    height: 0;
    padding-bottom: 50%;
}

.gauge-body {
    width: 100%;
    height: 100%;
    position: relative;
    border-top-left-radius: 100% 200%;
    border-top-right-radius: 100% 200%;
    overflow: hidden;
    background-color: #f5f5f5;
    border: 5px solid #e0e0e0;
    border-bottom: none;
}

.gauge-fill {
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, #00c6ff 0%, #0072ff 100%);
    transform-origin: center top;
    transform: rotate(0.5turn);
    transition: transform 1s ease-out;
}

.gauge-cover {
    width: 75%;
    height: 150%;
    background: white;
    border-radius: 50%;
    position: absolute;
    top: 25%;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.8rem;
    font-weight: bold;
    color: #333;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1) inset;
}

.gauge-labels {
    display: flex;
    justify-content: space-between;
    padding: 10px 15px;
    font-size: 0.9rem;
    color: #666;
}

.test-info {
    display: flex;
    justify-content: space-around;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 30px;
}

.info-box {
    background: white;
    border-radius: 10px;
    padding: 20px;
    width: 150px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
    transition: transform 0.3s;
}

.info-box:hover {
    transform: translateY(-5px);
}

.info-box i {
    font-size: 2rem;
    margin-bottom: 10px;
    color: #4b6cb7;
}

.info-box h3 {
    margin-bottom: 10px;
    color: #444;
}

.info-box .speed {
    font-size: 1.3rem;
    font-weight: bold;
    margin-bottom: 5px;
    color: #0072ff;
}

.info-box .status {
    font-size: 0.9rem;
    color: #666;
}

.test-btn {
    background: linear-gradient(135deg, #4b6cb7 0%, #182848 100%);
    color: white;
    border: none;
    padding: 12px 30px;
    font-size: 1.1rem;
    border-radius: 50px;
    cursor: pointer;
    transition: all 0.3s;
    box-shadow: 0 5px 15px rgba(75, 108, 183, 0.4);
}

.test-btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 20px rgba(75, 108, 183, 0.6);
}

.test-btn:active {
    transform: translateY(1px);
}

.test-btn i {
    margin-right: 8px;
}

/* History Container */
.history-container {
    padding: 0 30px 30px;
}

.history-container h2 {
    margin-bottom: 20px;
    color: #444;
    display: flex;
    align-items: center;
    gap: 10px;
}

.history-container h2 i {
    color: #4b6cb7;
}

table {
    width: 100%;
    border-collapse: collapse;
    background: white;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
}

th, td {
    padding: 12px 15px;
    text-align: left;
    border-bottom: 1px solid #eee;
}

th {
    background-color: #4b6cb7;
    color: white;
}

tr:nth-child(even) {
    background-color: #f9f9f9;
}

tr:hover {
    background-color: #f1f1f1;
}

/* Footer */
footer {
    text-align: center;
    padding: 20px;
    background: #f5f5f5;
    color: #666;
    font-size: 0.9rem;
}

/* Responsive Design */
@media (max-width: 768px) {
    .container {
        border-radius: 0;
    }
    
    .test-info {
        flex-direction: column;
        align-items: center;
    }
    
    .info-box {
        width: 100%;
        max-width: 250px;
    }
    
    th, td {
        padding: 8px 10px;
        font-size: 0.9rem;
    }
}

/* Animations */
@keyframes pulse {
    0% { transform: scale(1); }
    50% { transform: scale(1.05); }
    100% { transform: scale(1); }
}

.testing {
    animation: pulse 1.5s infinite;
}

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

.spinner {
    display: inline-block;
    animation: spin 1s linear infinite;
    margin-right: 8px;
}

3. JavaScript (script.js)

javascript
document.addEventListener('DOMContentLoaded', function() {
    // DOM Elements
    const startTestBtn = document.getElementById('startTestBtn');
    const gaugeFill = document.getElementById('gaugeFill');
    const speedValue = document.getElementById('speedValue');
    const downloadInfo = document.getElementById('downloadInfo');
    const uploadInfo = document.getElementById('uploadInfo');
    const pingInfo = document.getElementById('pingInfo');
    const historyTable = document.getElementById('historyTable').getElementsByTagName('tbody')[0];

    // Load history from localStorage
    let testHistory = JSON.parse(localStorage.getItem('speedTestHistory')) || [];
    renderHistory();

    // Start Test Button Click Event
    startTestBtn.addEventListener('click', function() {
        startTestBtn.disabled = true;
        startTestBtn.innerHTML = '<i class="fas fa-spinner spinner"></i> Mengukur...';
        startTestBtn.classList.add('testing');

        // Reset UI
        resetTestUI();

        // Run tests sequentially
        runTests()
            .then(results => {
                // Save results to history
                const testResult = {
                    date: new Date().toLocaleString(),
                    download: results.download,
                    upload: results.upload,
                    ping: results.ping
                };
                
                testHistory.unshift(testResult);
                if (testHistory.length > 10) testHistory.pop();
                
                localStorage.setItem('speedTestHistory', JSON.stringify(testHistory));
                renderHistory();
            })
            .catch(error => {
                console.error('Test error:', error);
                downloadInfo.querySelector('.status').textContent = 'Error: ' + error.message;
            })
            .finally(() => {
                startTestBtn.disabled = false;
                startTestBtn.innerHTML = '<i class="fas fa-redo"></i> Test Lagi';
                startTestBtn.classList.remove('testing');
            });
    });

    // Function to run all tests
    async function runTests() {
        const results = {};
        
        // Ping Test
        results.ping = await testPing();
        updatePingUI(results.ping);
        
        // Download Test
        results.download = await testDownload();
        updateDownloadUI(results.download);
        
        // Upload Test
        results.upload = await testUpload();
        updateUploadUI(results.upload);
        
        return results;
    }

    // Ping Test (simulated)
    function testPing() {
        return new Promise(resolve => {
            setTimeout(() => {
                // Simulate ping between 10ms and 100ms
                const ping = Math.floor(Math.random() * 90) + 10;
                resolve(ping);
            }, 1000);
        });
    }

    // Download Test (simulated)
    function testDownload() {
        return new Promise(resolve => {
            let progress = 0;
            const maxSpeed = 50 + Math.random() * 150; // Random max speed between 50-200 Mbps
            const interval = setInterval(() => {
                progress += 5;
                const currentSpeed = Math.min(maxSpeed, progress);
                updateGauge(currentSpeed);
                updateDownloadUI(currentSpeed);
                
                if (progress >= maxSpeed) {
                    clearInterval(interval);
                    setTimeout(() => resolve(maxSpeed), 500);
                }
            }, 100);
        });
    }

    // Upload Test (simulated)
    function testUpload() {
        return new Promise(resolve => {
            let progress = 0;
            // Upload is typically slower than download
            const maxSpeed = 10 + Math.random() * (Math.random() * 50); 
            const interval = setInterval(() => {
                progress += 2;
                const currentSpeed = Math.min(maxSpeed, progress);
                updateGauge(currentSpeed);
                updateUploadUI(currentSpeed);
                
                if (progress >= maxSpeed) {
                    clearInterval(interval);
                    setTimeout(() => resolve(maxSpeed), 500);
                }
            }, 100);
        });
    }

    // UI Update Functions
    function resetTestUI() {
        gaugeFill.style.transform = 'rotate(0.5turn)';
        speedValue.textContent = '0 Mbps';
        
        downloadInfo.querySelector('.speed').textContent = '-';
        downloadInfo.querySelector('.status').textContent = 'Mengukur...';
        
        uploadInfo.querySelector('.speed').textContent = '-';
        uploadInfo.querySelector('.status').textContent = 'Menunggu...';
        
        pingInfo.querySelector('.speed').textContent = '-';
        pingInfo.querySelector('.status').textContent = 'Menunggu...';
    }

    function updateGauge(speed) {
        const percentage = Math.min(speed / 2, 100); // Cap at 200 Mbps (100% of gauge)
        const rotation = 0.5 + (percentage / 100);
        gaugeFill.style.transform = `rotate(${rotation}turn)`;
        speedValue.textContent = speed.toFixed(1) + ' Mbps';
    }

    function updateDownloadUI(speed) {
        downloadInfo.querySelector('.speed').textContent = speed.toFixed(1) + ' Mbps';
        downloadInfo.querySelector('.status').textContent = 'Download selesai';
    }

    function updateUploadUI(speed) {
        uploadInfo.querySelector('.speed').textContent = speed.toFixed(1) + ' Mbps';
        uploadInfo.querySelector('.status').textContent = 'Upload selesai';
    }

    function updatePingUI(ping) {
        pingInfo.querySelector('.speed').textContent = ping + ' ms';
        pingInfo.querySelector('.status').textContent = 'Ping selesai';
    }

    // History Functions
    function renderHistory() {
        historyTable.innerHTML = '';
        
        testHistory.forEach(test => {
            const row = historyTable.insertRow();
            
            const dateCell = row.insertCell(0);
            dateCell.textContent = test.date;
            
            const downloadCell = row.insertCell(1);
            downloadCell.textContent = test.download.toFixed(1) + ' Mbps';
            
            const uploadCell = row.insertCell(2);
            uploadCell.textContent = test.upload.toFixed(1) + ' Mbps';
            
            const pingCell = row.insertCell(3);
            pingCell.textContent = test.ping + ' ms';
        });
    }
});

Fitur Utama

  1. Pengukuran Tiga Parameter:
    • Kecepatan Download (Mbps)
    • Kecepatan Upload (Mbps)
    • Latensi Ping (ms)
  2. Visualisasi Data:
    • Gauge meter interaktif
    • Tampilan kartu informasi
    • Animasi real-time saat pengukuran
  3. Penyimpanan Histori:
    • Menyimpan 10 hasil terakhir
    • Tampilan dalam format tabel
    • Penyimpanan menggunakan localStorage

Cara Menggunakan

  1. Buka file index.html di browser
  2. Klik tombol “Mulai Test”
  3. Tunggu proses pengukuran selesai
  4. Lihat hasil di gauge meter dan tabel histori


Pengembangan Lebih Lanjut

  1. Integrasi dengan API speed test nyata
  2. Grafik histori perkembangan kecepatan

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *