Project
Tabular Risk
Client-side risk scoring system demonstrating transparent machine learning with interpretable logistic regression and optional API integration.
What This Does
This is a risk scoring tool that runs entirely in your browser. You enter basic financial data (age, income, debt ratio, late payments), and it calculates a risk score from 0-100%. The calculation happens on your device—no data is sent anywhere unless you explicitly choose to test the optional API mode.
It solves the problem of opaque risk assessment by using a simple, transparent mathematical model where you can see exactly how each input affects the final score. This is the opposite of a black-box machine learning system where the decision logic is hidden.
This project demonstrates three key capabilities: building interpretable machine learning models that humans can audit, implementing complex calculations efficiently in client-side JavaScript, and designing flexible systems that work both standalone and as API integrations.
Technical Overview
Purpose and Scope
This system implements a client-side risk scoring engine for tabular financial data. The primary goal is demonstrating interpretable machine learning through a transparent baseline model, contrasted with an optional API integration path for more sophisticated models. The scope is deliberately limited to a four-feature input space to maintain clarity and auditability.
Architecture and Design Decisions
The architecture is a single-page application with zero dependencies. Local scoring uses a logistic regression model with hand-tuned weights (not trained on data), chosen specifically for interpretability. Each feature contribution to the final risk score is exposed in the output, allowing complete transparency into the decision process. The API mode provides a comparison point, demonstrating how the same interface can work with either a simple baseline or a deployed model endpoint.
Data Flow and Execution Model
In local mode: User inputs are read from form fields, validated, normalized to [0,1] ranges, multiplied by fixed weights, summed with a bias term, passed through a sigmoid activation function, and rendered as both a visual progress bar and structured JSON output. The entire pipeline executes synchronously in under 1ms. In API mode: Inputs are serialized to JSON, sent via HTTP POST to a configurable endpoint, and the response is parsed and displayed with the same rendering logic.
Implementation Details
The logistic model uses these weights: age (+0.35), income (-1.10), debt ratio (+1.55), late payments (+1.25), with a bias of -0.35. Inputs are normalized: age divided by 100, income by 150,000, debt ratio used directly, late payments divided by 12. The sigmoid function converts the linear combination into a probability. Risk thresholds: below 33% is low (green), 33-66% is medium (yellow), above 66% is high (red). The UI synchronizes number inputs with range sliders bidirectionally using event listeners.
Technology Stack
Languages: Vanilla JavaScript (ES6+) for logic and DOM manipulation, HTML5 for structure, CSS3 for styling with CSS Grid and custom properties. Why: Zero build step, no dependencies, maximum portability, minimal load time, complete browser compatibility without transpilation.
Model: Logistic regression with explicit weight specification. Why: Fully interpretable, no training data required, deterministic behavior, easy to audit, suitable for demonstration and baseline comparison.
API Integration: Fetch API with AbortController for timeout handling, JSON serialization. Why: Native browser support, modern async/await patterns, no external HTTP library needed, allows graceful degradation when API is unavailable.
UI Components: Custom-built form controls, synchronized number/range inputs, preset buttons, real-time status indicators. Why: Full control over behavior and appearance, no framework overhead, teaches fundamental DOM manipulation patterns.
Technical Challenges and Trade-offs
Challenge: Maintaining interpretability while providing meaningful risk scores. Trade-off: Used hand-tuned weights instead of training on real data, sacrificing predictive accuracy for complete transparency and auditability.
Challenge: Supporting both local and API modes with a unified interface. Trade-off: Duplicated some display logic but gained the ability to demonstrate both lightweight and full-stack deployment scenarios without breaking changes.
Challenge: Client-side JavaScript has no true type safety. Trade-off: Implemented explicit validation and bounds checking at input time, accepting runtime overhead to prevent invalid states from propagating through calculations.
Challenge: API calls may fail or timeout. Trade-off: Used AbortController with a 12-second timeout and structured error handling, prioritizing user feedback over silent failures, at the cost of added code complexity.
Interactive Demo
Try it
Local mode uses a small logistic baseline (hand-tuned weights) for interpretability. API mode expects POST /tabular/score (optional).
Output
{}
Links
RepositoryGitHubAPI shape (optional)
{
"age": 35,
"income_gbp": 42000,
"debt_ratio": 0.28,
"late_payments": 1
}
Notes
- Local scoring is deterministic and intentionally simple.
- API mode uses the same payload and returns JSON.
- Values are illustrative — used only to demonstrate the workflow.