BACK_TO_LOGS// STEP_8_OF_10
HANDBOOK_GUIDE2026-06-29

Rising Waters: Machine Learning Approach to Flood Prediction System ML Guide

By Rayan Syed
45 min read

Part 8: Frontend User Interface Files

To create a clean interface, we split our frontend assets into three files: index.html, style.css, and script.js.

8.1 How the Frontend Connects with the Backend

Web applications operate on a Client-Server Architecture.

  • The Client (Frontend): This is the user interface running in the browser (index.html, style.css, script.js). Its job is to collect information from the user and display results.
  • The Server (Backend): This is our Flask script (app.py) running on our local machine. It listens for incoming HTTP requests, feeds features to our machine learning model, and returns the prediction.
  • The Communication (HTTP POST & JSON): When the user clicks the submit button, the frontend does not reload the page. Instead, JavaScript packages the form inputs into a JSON dictionary and sends it via an HTTP POST request to the /predict URL on the Flask server. Once the server responds with a success or rejection state, the webpage updates itself dynamically.
  • Why split into HTML, CSS, and JS?: We separate structure (HTML), style (CSS), and behavior (JavaScript) to maintain clean code. This follows the Separation of Concerns principle, making it easy to redesign the UI or tweak frontend validations without editing backend code.

Frontend Template (templates/index.html)

Save this layout configuration inside templates/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flood Prediction System</title>
    <link rel="stylesheet" href="/static/style.css">
</head>
<body>
    <div class="container">
        <h2>Flood Risk Prediction System</h2>
        <form id="predictionForm">
            <label for="temp">Average Temperature (°C):</label>
            <input type="number" id="temp" step="0.1" required>

            <label for="humidity">Relative Humidity (%):</label>
            <input type="number" id="humidity" min="0" max="100" required>

            <label for="cloud_cover">Cloud Cover (%):</label>
            <input type="number" id="cloud_cover" min="0" max="100" required>

            <label for="annual_rainfall">Annual Rainfall (mm):</label>
            <input type="number" id="annual_rainfall" step="0.1" required>

            <label for="jan_feb">Winter Rainfall (Jan-Feb) (mm):</label>
            <input type="number" id="jan_feb" step="0.1" required>

            <label for="mar_may">Summer Rainfall (Mar-May) (mm):</label>
            <input type="number" id="mar_may" step="0.1" required>

            <label for="jun_sep">Monsoon Rainfall (Jun-Sep) (mm):</label>
            <input type="number" id="jun_sep" step="0.1" required>

            <label for="oct_dec">Autumn Rainfall (Oct-Dec) (mm):</label>
            <input type="number" id="oct_dec" step="0.1" required>

            <label for="avg_june">Average June Rainfall (mm):</label>
            <input type="number" id="avg_june" step="0.1" required>

            <label for="sub_index">Sub-Division Basin Index:</label>
            <input type="number" id="sub_index" step="0.1" required>

            <button type="submit">Check Flood Risk</button>
        </form>
        <div id="result">
            <div id="riskLevel"></div>
            <div id="probabilityText"></div>
        </div>
    </div>
    <script src="/static/script.js"></script>
</body>
</html>

Style Sheet (static/style.css)

Save this CSS configuration inside static/style.css to build our form wrapper:

body {
    background-color: #0a0a0c;
    color: #e4e4e7;
    font-family: system-ui, -apple-system, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    margin: 0;
    padding: 20px 0;
}

.container {
    background-color: #121214;
    border: 1px solid #27272a;
    padding: 32px;
    border-radius: 16px;
    width: 380px;
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
}

h2 {
    text-align: center;
    margin-bottom: 24px;
    font-size: 1.5rem;
    color: #ffffff;
}

label {
    display: block;
    margin-bottom: 6px;
    font-size: 0.85rem;
    font-weight: 500;
    color: #a1a1aa;
}

input, select, button {
    width: 100%;
    margin-bottom: 16px;
    padding: 10px;
    border-radius: 8px;
    border: 1px solid #27272a;
    background-color: #1a1a1e;
    color: #ffffff;
    box-sizing: border-box;
    font-size: 0.9rem;
    transition: all 0.2s;
}

input:focus, select:focus {
    border-color: #3b82f6;
    outline: none;
}

button {
    background-color: #2563eb;
    color: white;
    border: none;
    font-weight: 650;
    cursor: pointer;
    margin-top: 10px;
}

button:hover {
    background-color: #1d4ed8;
}

#result {
    margin-top: 15px;
    padding: 16px;
    border-radius: 8px;
    text-align: center;
    font-size: 0.95rem;
    display: none;
}

.success {
    background-color: rgba(16, 185, 129, 0.15);
    border: 1px solid rgba(16, 185, 129, 0.4);
    color: #34d399;
}

.danger {
    background-color: rgba(239, 68, 68, 0.15);
    border: 1px solid rgba(239, 68, 68, 0.4);
    color: #f87171;
}

#riskLevel {
    font-size: 1.1rem;
    font-weight: 850;
    margin-bottom: 8px;
}

#probabilityText {
    font-size: 0.85rem;
    opacity: 0.9;
}

Application Logic Script (static/script.js)

Save this JS module inside static/script.js:

document.getElementById('predictionForm').addEventListener('submit', async (e) => {
    e.preventDefault();

    const resultDiv = document.getElementById('result');
    const riskLevel = document.getElementById('riskLevel');
    const probText = document.getElementById('probabilityText');
    resultDiv.style.display = 'none';

    // Pack input form values into a JSON payload
    const payload = {
        temp: document.getElementById('temp').value,
        humidity: document.getElementById('humidity').value,
        cloud_cover: document.getElementById('cloud_cover').value,
        annual_rainfall: document.getElementById('annual_rainfall').value,
        jan_feb: document.getElementById('jan_feb').value,
        mar_may: document.getElementById('mar_may').value,
        jun_sep: document.getElementById('jun_sep').value,
        oct_dec: document.getElementById('oct_dec').value,
        avg_june: document.getElementById('avg_june').value,
        sub_index: document.getElementById('sub_index').value
    };

    try {
        // Send POST request containing payload to the prediction endpoint
        const response = await fetch('/predict', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
        });

        const result = await response.json();
        
        resultDiv.style.display = 'block';
        if (result.flood) {
            resultDiv.className = 'danger';
            riskLevel.innerText = 'WARNING: HIGH FLOOD RISK DETECTED';
            probText.innerText = `Flood Probability: ${result.flood_probability.toFixed(2)}% | Safety Chance: ${result.no_flood_probability.toFixed(2)}%`;
        } else {
            resultDiv.className = 'success';
            riskLevel.innerText = 'STATUS: SAFE - LOW FLOOD RISK';
            probText.innerText = `Flood Probability: ${result.flood_probability.toFixed(2)}% | Safety Chance: ${result.no_flood_probability.toFixed(2)}%`;
        }
    } catch (err) {
        resultDiv.style.display = 'block';
        resultDiv.className = 'danger';
        riskLevel.innerText = 'Error processing API request.';
        probText.innerText = '';
        console.error(err);
    }
});

8.2 Understanding the Script Execution

Let’s break down the logic of this script:

  • e.preventDefault(): Prevents the browser from reloading the page when you click the submit button. This allows us to handle the form processing silently in the background.
  • payload: Gathers all values from form fields (e.g. temp and humidity) and formats them into a single JavaScript object. The keys in this object match the keys in our Python request parser inside app.py.
  • fetch('/predict', ...): Starts a secure asynchronous HTTP connection. It sends the payload to our backend server as a JSON string (JSON.stringify(payload)) with headers identifying the content type.
  • response.json(): Waits for the server to reply and parses the returned JSON string back into a readable JavaScript dictionary.
  • Result Banner Update: Displays the #result container and applies the appropriate CSS styles. If result.flood is true, it displays a red warning banner; if false, it shows a green safety banner.