Introduction
Gauge charts are ideal for displaying a single metric such as system performance, CPU usage, or progress toward a goal.
They visually represent how a value compares to defined ranges or thresholds, making them particularly effective for dashboards and monitoring applications.
In Python, gauge charts can be created using several libraries depending on your needs:
- Plotly: interactive and ready to use in notebooks or dashboards
- Matplotlib: static and highly customizable, ideal for publications
- Dash: for live, interactive web apps that can update in real time
Below we’ll go through each approach, from simple static visualizations to real-time interactive dashboards.
Using Plotly
Plotly provides a dedicated chart type for gauges via the go.Indicator object.
It combines numeric indicators and gauge arcs, with built-in support for color segments, threshold markers, and labels.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | import plotly.graph_objects as go value = 75 fig = go.Figure(go.Indicator( mode = "gauge+number", value = value, title = { 'text': "System Performance", 'font': {'size': 22, 'color': '#2F4F4F'} # dark slate gray }, number = {'font': {'size': 40, 'color': '#2F4F4F'}}, gauge = { 'axis': { 'range': [0, 100], 'tickwidth': 1, 'tickcolor': '#A9A9A9' }, 'bar': {'color': '#4C78A8'}, # muted blue for the needle bar 'bgcolor': 'white', 'borderwidth': 1.5, 'bordercolor': '#D3D3D3', 'steps': [ {'range': [0, 50], 'color': '#E5E8E8'}, # light gray {'range': [50, 80], 'color': '#C8D6E5'}, # pale blue-gray {'range': [80, 100], 'color': '#A3C1AD'} # desaturated green ], 'threshold': { 'line': {'color': '#FF6F61', 'width': 4}, # muted coral 'thickness': 0.75, 'value': 90 } } )) fig.update_layout( paper_bgcolor='white', font={'color': '#2F4F4F', 'family': 'Arial'}, ) fig.show() |
Explanation:
This example creates a smooth, minimalistic gauge with three color bands representing performance ranges.
The threshold highlights a critical value (90), while the muted blue needle and light palette ensure readability in professional dashboards.
Features:
- Built-in gauge support via
go.Indicator - Fully interactive in Jupyter, VS Code, or web dashboards
- Easily customizable color segments, fonts, and thresholds

Using Matplotlib (Static, More Control)
If you prefer static charts or need publication-quality figures, Matplotlib provides full control.
While it doesn’t include a dedicated gauge type, we can build one using arcs (patches.Wedge) and polar geometry.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import matplotlib.pyplot as plt import matplotlib.patches as patches import numpy as np def gauge_chart(value, title='System Performance', min_val=0, max_val=100): # Clamp the input value value = max(min_val, min(max_val, value)) # Convert value to angular position (0° = max, 180° = min) def val_to_deg(v): return 180.0 * (1.0 - (v - min_val) / (max_val - min_val)) fig, ax = plt.subplots(figsize=(6, 3)) ax.set_aspect('equal') # Define geometry outer_r = 1.0 thickness = 0.30 inner_r = outer_r - thickness # Color zones zones = [ (0, 50, '#E5E8E8'), # light gray (50, 80, '#C8D6E5'), # pale blue-gray (80, 100, '#A3C1AD') # desaturated green ] # Draw each zone as a wedge for a, b, color in zones: theta1 = val_to_deg(b) theta2 = val_to_deg(a) wedge = patches.Wedge(center=(0, 0), r=outer_r, theta1=theta1, theta2=theta2, width=thickness, facecolor=color, edgecolor='white', linewidth=1) ax.add_patch(wedge) # Subtle border behind the arc bg_wedge = patches.Wedge(center=(0, 0), r=outer_r + 0.01, theta1=0, theta2=180, width=thickness + 0.02, facecolor='none', edgecolor='#DDDDDD', linewidth=1) ax.add_patch(bg_wedge) # Draw the needle angle_deg = val_to_deg(value) angle_rad = np.deg2rad(angle_deg) needle_len = inner_r + thickness * 0.9 nx, ny = needle_len * np.cos(angle_rad), needle_len * np.sin(angle_rad) ax.plot([0, nx], [0, ny], lw=3.5, color='#4C78A8', zorder=5) ax.scatter([0], [0], s=120, color='#2F4F4F', zorder=6) # Threshold marker (at 90%) threshold = 90 th_deg = val_to_deg(threshold) th_rad = np.deg2rad(th_deg) t_outer, t_inner = outer_r + 0.01, outer_r - 0.02 tx1, ty1 = t_inner * np.cos(th_rad), t_inner * np.sin(th_rad) tx2, ty2 = t_outer * np.cos(th_rad), t_outer * np.sin(th_rad) ax.plot([tx1, tx2], [ty1, ty2], lw=3, color='#FF6F61', zorder=7, solid_capstyle='round') # Labels and text ax.text(0, -0.20, f'{value:.0f}%', ha='center', va='center', fontsize=28, fontweight='bold', color='#2F4F4F') ax.text(0, -0.36, title, ha='center', va='center', fontsize=12, color='#2F4F4F') # Tick labels (0, 50, 100) for tick in [min_val, (min_val + max_val) / 2, max_val]: d = val_to_deg(tick) r_tick = outer_r + 0.07 x, y = r_tick * np.cos(np.deg2rad(d)), r_tick * np.sin(np.deg2rad(d)) ax.text(x, y, f'{int(tick)}', ha='center', va='center', fontsize=10, color='#666666') ax.set_xlim(-1.15, 1.15) ax.set_ylim(-0.35, 1.15) ax.axis('off') plt.tight_layout() plt.show() # Example gauge_chart(75, title='System Performance') |
Explanation:
Here, the gauge is constructed from wedges and trigonometric angles.
This approach allows fine-grained control over every visual element (e.g., radius, border width, or colors), making it ideal for reports or publications.

Using Dash (for Web Apps)
To bring your gauge charts to the web or a monitoring dashboard, you can use Dash, which integrates seamlessly with Plotly figures.
Here’s a basic Dash setup displaying a static gauge:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from dash import Dash, dcc, html import plotly.graph_objects as go app = Dash(__name__) fig = go.Figure(go.Indicator( mode = "gauge+number", value = 72, title = {'text': "CPU Usage"}, gauge = {'axis': {'range': [0, 100]}} )) app.layout = html.Div([ html.H2("Real-time Gauge Example"), dcc.Graph(figure=fig) ]) if __name__ == '__main__': app.run_server(debug=True) |
Explanation:
This example sets up a minimal dashboard with a single Plotly gauge embedded inside a Dash layout.
Running it locally launches an interactive web app at http://127.0.0.1:8050.

Improved Dash Gauge App (Polished UI + Real-time Option Ready)
This enhanced version demonstrates dynamic updating, suitable for real-time metrics like CPU usage or sensor data.
It uses dcc.Interval to refresh the gauge periodically.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | from dash import Dash, dcc, html from dash.dependencies import Input, Output import plotly.graph_objects as go import random # ---------------------------- # Create Dash app # ---------------------------- app = Dash(__name__) app.title = "System Monitor" # ---------------------------- # Define gauge figure (base style) # ---------------------------- def create_gauge(value): fig = go.Figure(go.Indicator( mode="gauge+number", value=value, title={'text': "CPU Usage", 'font': {'size': 22, 'color': '#2F4F4F'}}, number={'font': {'size': 36, 'color': '#2F4F4F'}}, gauge={ 'axis': {'range': [0, 100], 'tickwidth': 1, 'tickcolor': '#A9A9A9'}, 'bar': {'color': '#4C78A8'}, # muted blue 'bgcolor': 'white', 'borderwidth': 1.5, 'bordercolor': '#D3D3D3', 'steps': [ {'range': [0, 50], 'color': '#E5E8E8'}, {'range': [50, 80], 'color': '#C8D6E5'}, {'range': [80, 100], 'color': '#A3C1AD'} ], 'threshold': { 'line': {'color': '#FF6F61', 'width': 4}, 'thickness': 0.75, 'value': 90 } } )) fig.update_layout( margin=dict(l=20, r=20, t=60, b=20), paper_bgcolor='white', font={'color': '#2F4F4F', 'family': 'Arial'} ) return fig # ---------------------------- # Layout # ---------------------------- app.layout = html.Div([ html.H2("Real-Time Gauge Example", style={'textAlign': 'center', 'color': '#2F4F4F', 'marginBottom': '20px'}), dcc.Graph(id='gauge-graph', figure=create_gauge(72), style={'height': '70vh', 'width': '60%', 'margin': 'auto'}), # Refresh every second (simulate real-time) dcc.Interval(id='interval', interval=1000, n_intervals=0) ], style={ 'backgroundColor': 'white', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 'justifyContent': 'center', 'height': '100vh' }) # ---------------------------- # Callback for real-time updates # ---------------------------- @app.callback( Output('gauge-graph', 'figure'), Input('interval', 'n_intervals') ) def update_gauge(n): # Simulate dynamic CPU usage new_value = random.randint(40, 95) return create_gauge(new_value) # ---------------------------- # Run app # ---------------------------- if __name__ == '__main__': app.run_server(debug=True) |
Explanation:
This app uses the dcc.Interval component to update the gauge each second, simulating a live data stream.
The layout is minimalist, centered, and easily customizable — perfect for system monitoring dashboards.

References
| Links | Site |
|---|---|
| Plotly Indicator (Gauge) documentation | Plotly official site |
Plotly go.Indicator API reference |
Plotly GitHub documentation |
| Plotly Gauge charts (with steps and threshold) | Plotly official examples |
Dash dcc.Graph component |
Dash documentation |
Dash dcc.Interval component |
Dash documentation |
| Dash Layout and Callbacks | Dash official guide |
| Dash HTML Components | Dash documentation |
