Show code
viewof examplePreset = Inputs.select(
["Custom", "Autonomous Car (Collision)", "Smart Meter (Hourly)", "Video Analytics (Real-time)", "Industrial Sensor (Vibration)", "Wearable Health Monitor"],
{
label: "Reference Scenario",
value: "Custom"
}
)
// Show preset reference values
presetInfo = {
const presets = {
"Custom": { dataSize: "---", complexity: "---", bandwidth: "---", distance: "---", latency: "---", description: "Configure your own scenario below" },
"Autonomous Car (Collision)": { dataSize: "50 KB", complexity: "Very High", bandwidth: "1000 Mbps", distance: "50 km", latency: "10 ms", description: "Safety-critical collision avoidance requiring edge processing" },
"Smart Meter (Hourly)": { dataSize: "1 KB", complexity: "Low", bandwidth: "1 Mbps", distance: "2000 km", latency: "500 ms", description: "Low-data hourly readings, cloud-suitable" },
"Video Analytics (Real-time)": { dataSize: "500 KB", complexity: "High", bandwidth: "100 Mbps", distance: "100 km", latency: "50 ms", description: "Security camera object detection, fog optimal" },
"Industrial Sensor (Vibration)": { dataSize: "10 KB", complexity: "Medium", bandwidth: "50 Mbps", distance: "200 km", latency: "20 ms", description: "Factory equipment monitoring with anomaly detection" },
"Wearable Health Monitor": { dataSize: "5 KB", complexity: "Low", bandwidth: "10 Mbps", distance: "1000 km", latency: "200 ms", description: "Fitness tracker with smartphone aggregation" }
};
return presets[examplePreset];
}
// Show reference configuration when a scenario is selected
html`
<div style="margin-bottom: 20px; padding: 12px; background: ${examplePreset === "Custom" ? '#f8f9fa' : '#e3f2fd'}; border-radius: 8px; border-left: 4px solid ${examplePreset === "Custom" ? '#7f8c8d' : '#2196F3'};">
<strong>${examplePreset}</strong>: ${presetInfo.description}
${examplePreset !== "Custom" ? html`
<br><span style="font-size: 13px; color: #555;">
Reference: ${presetInfo.dataSize} data, ${presetInfo.complexity} complexity, ${presetInfo.bandwidth} bandwidth, ${presetInfo.distance} distance, ${presetInfo.latency} latency requirement
</span>
` : ''}
</div>
`Show code
viewof dataSize = Inputs.range([1, 1000], {
label: "Data Size (KB)",
step: 1,
value: 100
})
viewof processingComplexity = Inputs.select(
["Low", "Medium", "High", "Very High"],
{
label: "Processing Complexity",
value: "Medium"
}
)
viewof networkBandwidth = Inputs.range([1, 1000], {
label: "Network Bandwidth (Mbps)",
step: 1,
value: 100
})
viewof distanceToCloud = Inputs.range([10, 5000], {
label: "Distance to Cloud (km)",
step: 10,
value: 500
})
viewof networkCondition = Inputs.select(
["Excellent", "Good", "Fair", "Poor"],
{
label: "Network Condition",
value: "Good"
}
)
viewof latencyRequirement = Inputs.range([1, 500], {
label: "Latency Requirement (ms)",
step: 1,
value: 100
})
viewof requestsPerDay = Inputs.range([1, 86400], {
label: "Requests per Day",
step: 1,
value: 1440
})
// Processing time based on complexity and tier capabilities
processingTimes = {
const complexityMultipliers = {
"Low": 1,
"Medium": 5,
"High": 20,
"Very High": 100
};
const baseProcessing = complexityMultipliers[processingComplexity];
return {
edge: baseProcessing * 10, // Limited compute power (1-10 GFLOPS)
fog: baseProcessing * 2, // Moderate compute (100-1000 GFLOPS)
cloud: baseProcessing * 0.5 // Massive compute (1000+ TFLOPS)
};
}
// Network latency based on conditions, distance, and bandwidth
networkLatencies = {
const conditionMultipliers = {
"Excellent": 1.0,
"Good": 1.5,
"Fair": 2.5,
"Poor": 4.0
};
const multiplier = conditionMultipliers[networkCondition];
// Transmission time = data size / bandwidth (KB to bits, Mbps to bps)
const transmissionTime = (dataSize * 8) / (networkBandwidth * 1000) * 1000; // ms
// Propagation delay: ~5ms per 1000km (speed of light in fiber ~200,000 km/s)
const propagationDelay = (distanceToCloud / 1000) * 5;
// Edge: local processing, minimal network
// Fog: regional, ~10% of distance to cloud
// Cloud: full distance
return {
edge: {
uplink: 1 * multiplier + transmissionTime * 0.1,
downlink: 1 * multiplier,
propagation: 0.5
},
fog: {
uplink: 3 * multiplier + transmissionTime * 0.5,
downlink: 3 * multiplier,
propagation: propagationDelay * 0.1
},
cloud: {
uplink: 10 * multiplier + transmissionTime,
downlink: 10 * multiplier,
propagation: propagationDelay
}
};
}
// Bandwidth cost calculation ($/month based on data volume)
bandwidthCosts = {
// Industry average costs per GB transferred
const costPerGB = {
edge: 0, // Local processing, no egress
fog: 0.02, // Regional transfer, minimal cost
cloud: 0.09 // Cloud egress (AWS/Azure average)
};
// Calculate monthly data volume: data size * requests per day * 30 days
const monthlyDataGB = (dataSize / 1024) * requestsPerDay * 30; // Convert KB to GB
// Calculate request frequency description
const requestInterval = requestsPerDay >= 86400 ? "every second" :
requestsPerDay >= 1440 ? "every minute" :
requestsPerDay >= 288 ? "every 5 minutes" :
requestsPerDay >= 24 ? "hourly" : "infrequently";
return {
edge: {
monthly: monthlyDataGB * costPerGB.edge,
perRequest: (dataSize / 1024 / 1024) * costPerGB.edge,
dataVolume: monthlyDataGB
},
fog: {
monthly: monthlyDataGB * costPerGB.fog,
perRequest: (dataSize / 1024 / 1024) * costPerGB.fog,
dataVolume: monthlyDataGB
},
cloud: {
monthly: monthlyDataGB * costPerGB.cloud,
perRequest: (dataSize / 1024 / 1024) * costPerGB.cloud,
dataVolume: monthlyDataGB
},
requestInterval: requestInterval
};
}
// Calculate total round-trip time for each tier (including propagation delay)
latencyResults = {
return {
edge: {
uplink: networkLatencies.edge.uplink,
processing: processingTimes.edge,
downlink: networkLatencies.edge.downlink,
propagation: networkLatencies.edge.propagation,
total: networkLatencies.edge.uplink + processingTimes.edge + networkLatencies.edge.downlink + networkLatencies.edge.propagation * 2
},
fog: {
uplink: networkLatencies.fog.uplink,
processing: processingTimes.fog,
downlink: networkLatencies.fog.downlink,
propagation: networkLatencies.fog.propagation,
total: networkLatencies.fog.uplink + processingTimes.fog + networkLatencies.fog.downlink + networkLatencies.fog.propagation * 2
},
cloud: {
uplink: networkLatencies.cloud.uplink,
processing: processingTimes.cloud,
downlink: networkLatencies.cloud.downlink,
propagation: networkLatencies.cloud.propagation,
total: networkLatencies.cloud.uplink + processingTimes.cloud + networkLatencies.cloud.downlink + networkLatencies.cloud.propagation * 2
}
};
}
// Determine optimal tier based on latency requirement
recommendation = {
const results = latencyResults;
const requirement = latencyRequirement;
// Check which tiers meet the requirement
const viable = [];
if (results.edge.total <= requirement) viable.push("Edge");
if (results.fog.total <= requirement) viable.push("Fog");
if (results.cloud.total <= requirement) viable.push("Cloud");
if (viable.length === 0) {
return {
tier: "None",
message: "No tier meets your latency requirement! Consider: (1) Reducing processing complexity, (2) Improving network conditions, (3) Relaxing latency requirements, or (4) Pre-processing/caching strategies.",
color: "#e74c3c"
};
}
// Recommend the most cost-effective tier that meets requirements
// Preference order: Fog > Cloud > Edge (fog balances cost/capability)
let recommended = viable[0];
let message = "";
let color = "";
if (viable.includes("Fog")) {
recommended = "Fog";
message = `Fog Computing recommended: Meets ${requirement}ms requirement with balanced compute (${results.fog.total.toFixed(1)}ms total). Fog offers optimal cost-performance trade-off for most IoT workloads.`;
color = "#16A085";
} else if (viable.includes("Cloud")) {
recommended = "Cloud";
message = `Cloud Computing recommended: Meets ${requirement}ms requirement (${results.cloud.total.toFixed(1)}ms total). Leverages massive compute power for complex processing. Edge/Fog too slow for this complexity level.`;
color = "#3498db";
} else if (viable.includes("Edge")) {
recommended = "Edge";
message = `Edge Computing required: Only edge meets strict ${requirement}ms requirement (${results.edge.total.toFixed(1)}ms total). Critical for ultra-low latency applications like collision avoidance, despite limited compute.`;
color = "#2C3E50";
}
return { tier: recommended, message, color };
}
// Visualization: Stacked bar chart
Plot.plot({
marginLeft: 80,
marginBottom: 60,
width: 700,
height: 400,
x: {
label: "Total Latency (ms)",
domain: [0, Math.max(latencyResults.edge.total, latencyResults.fog.total, latencyResults.cloud.total) * 1.2]
},
y: {
label: "Computing Tier",
domain: ["Edge", "Fog", "Cloud"]
},
marks: [
// Uplink bars
Plot.barX([
{ tier: "Edge", component: "Uplink", value: latencyResults.edge.uplink, color: "#95a5a6" },
{ tier: "Fog", component: "Uplink", value: latencyResults.fog.uplink, color: "#95a5a6" },
{ tier: "Cloud", component: "Uplink", value: latencyResults.cloud.uplink, color: "#95a5a6" }
], {
x: "value",
y: "tier",
fill: "color",
title: d => `${d.component}: ${d.value.toFixed(1)}ms`
}),
// Processing bars (offset by uplink)
Plot.barX([
{ tier: "Edge", component: "Processing", value: latencyResults.edge.processing, offset: latencyResults.edge.uplink, color: "#e67e22" },
{ tier: "Fog", component: "Processing", value: latencyResults.fog.processing, offset: latencyResults.fog.uplink, color: "#e67e22" },
{ tier: "Cloud", component: "Processing", value: latencyResults.cloud.processing, offset: latencyResults.cloud.uplink, color: "#e67e22" }
], {
x: d => d.offset,
x2: d => d.offset + d.value,
y: "tier",
fill: "color",
title: d => `${d.component}: ${d.value.toFixed(1)}ms`
}),
// Downlink bars (offset by uplink + processing)
Plot.barX([
{ tier: "Edge", component: "Downlink", value: latencyResults.edge.downlink, offset: latencyResults.edge.uplink + latencyResults.edge.processing, color: "#16a085" },
{ tier: "Fog", component: "Downlink", value: latencyResults.fog.downlink, offset: latencyResults.fog.uplink + latencyResults.fog.processing, color: "#16a085" },
{ tier: "Cloud", component: "Downlink", value: latencyResults.cloud.downlink, offset: latencyResults.cloud.uplink + latencyResults.cloud.processing, color: "#16a085" }
], {
x: d => d.offset,
x2: d => d.offset + d.value,
y: "tier",
fill: "color",
title: d => `${d.component}: ${d.value.toFixed(1)}ms`
}),
// Latency requirement line
Plot.ruleX([latencyRequirement], {
stroke: "#e74c3c",
strokeWidth: 2,
strokeDasharray: "5,5"
}),
// Total latency labels
Plot.text([
{ tier: "Edge", total: latencyResults.edge.total },
{ tier: "Fog", total: latencyResults.fog.total },
{ tier: "Cloud", total: latencyResults.cloud.total }
], {
x: d => d.total + 5,
y: "tier",
text: d => `${d.total.toFixed(1)}ms`,
textAnchor: "start",
fill: "#2c3e50",
fontWeight: "bold"
})
]
})Show code
html`
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px;">
<strong>Legend:</strong>
<span style="margin-left: 15px;">
<span style="display: inline-block; width: 12px; height: 12px; background: #95a5a6; margin-right: 5px;"></span>
Uplink (Network Transmission)
</span>
<span style="margin-left: 15px;">
<span style="display: inline-block; width: 12px; height: 12px; background: #e67e22; margin-right: 5px;"></span>
Processing
</span>
<span style="margin-left: 15px;">
<span style="display: inline-block; width: 12px; height: 12px; background: #16a085; margin-right: 5px;"></span>
Downlink (Response)
</span>
<span style="margin-left: 15px;">
<span style="display: inline-block; width: 12px; height: 12px; background: transparent; border: 2px dashed #e74c3c; margin-right: 5px;"></span>
Latency Requirement
</span>
</div>
`Show code
html`
<div style="margin-top: 20px;">
<h4>Latency Breakdown</h4>
<table style="width: 100%; border-collapse: collapse; font-size: 14px;">
<thead style="background: #ecf0f1;">
<tr>
<th style="padding: 10px; text-align: left; border: 1px solid #bdc3c7;">Tier</th>
<th style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">Uplink (ms)</th>
<th style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">Processing (ms)</th>
<th style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">Downlink (ms)</th>
<th style="padding: 10px; text-align: right; border: 1px solid #bdc3c7; font-weight: bold;">Total (ms)</th>
<th style="padding: 10px; text-align: center; border: 1px solid #bdc3c7;">Meets Requirement?</th>
</tr>
</thead>
<tbody>
<tr style="background: ${latencyResults.edge.total <= latencyRequirement ? '#d5f4e6' : '#ffffff'};">
<td style="padding: 10px; border: 1px solid #bdc3c7; font-weight: bold;">Edge</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.edge.uplink.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.edge.processing.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.edge.downlink.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7; font-weight: bold;">${latencyResults.edge.total.toFixed(1)}</td>
<td style="padding: 10px; text-align: center; border: 1px solid #bdc3c7;">${latencyResults.edge.total <= latencyRequirement ? 'Yes' : 'No'}</td>
</tr>
<tr style="background: ${latencyResults.fog.total <= latencyRequirement ? '#d5f4e6' : '#ffffff'};">
<td style="padding: 10px; border: 1px solid #bdc3c7; font-weight: bold;">Fog</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.fog.uplink.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.fog.processing.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.fog.downlink.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7; font-weight: bold;">${latencyResults.fog.total.toFixed(1)}</td>
<td style="padding: 10px; text-align: center; border: 1px solid #bdc3c7;">${latencyResults.fog.total <= latencyRequirement ? 'Yes' : 'No'}</td>
</tr>
<tr style="background: ${latencyResults.cloud.total <= latencyRequirement ? '#d5f4e6' : '#ffffff'};">
<td style="padding: 10px; border: 1px solid #bdc3c7; font-weight: bold;">Cloud</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.cloud.uplink.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.cloud.processing.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7;">${latencyResults.cloud.downlink.toFixed(1)}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #bdc3c7; font-weight: bold;">${latencyResults.cloud.total.toFixed(1)}</td>
<td style="padding: 10px; text-align: center; border: 1px solid #bdc3c7;">${latencyResults.cloud.total <= latencyRequirement ? 'Yes' : 'No'}</td>
</tr>
</tbody>
</table>
</div>
`Show code
html`
<div style="margin-top: 20px; padding: 15px; background: ${recommendation.color}15; border-left: 4px solid ${recommendation.color}; border-radius: 5px;">
<h4 style="margin-top: 0; color: ${recommendation.color};">Recommendation: ${recommendation.tier} Computing</h4>
<p style="margin-bottom: 0; line-height: 1.6;">${recommendation.message}</p>
</div>
`Show code
Plot.plot({
marginLeft: 80,
marginBottom: 60,
width: 700,
height: 250,
title: "Monthly Bandwidth Cost by Tier ($)",
x: {
label: "Monthly Cost ($)",
domain: [0, Math.max(bandwidthCosts.edge.monthly, bandwidthCosts.fog.monthly, bandwidthCosts.cloud.monthly) * 1.3 + 1]
},
y: {
label: "Computing Tier",
domain: ["Edge", "Fog", "Cloud"]
},
marks: [
Plot.barX([
{ tier: "Edge", value: bandwidthCosts.edge.monthly, color: "#2C3E50" },
{ tier: "Fog", value: bandwidthCosts.fog.monthly, color: "#16A085" },
{ tier: "Cloud", value: bandwidthCosts.cloud.monthly, color: "#7F8C8D" }
], {
x: "value",
y: "tier",
fill: "color",
title: d => `$${d.value.toFixed(2)}/month`
}),
Plot.text([
{ tier: "Edge", value: bandwidthCosts.edge.monthly },
{ tier: "Fog", value: bandwidthCosts.fog.monthly },
{ tier: "Cloud", value: bandwidthCosts.cloud.monthly }
], {
x: d => d.value + 0.5,
y: "tier",
text: d => `$${d.value.toFixed(2)}`,
textAnchor: "start",
fill: "#2c3e50",
fontWeight: "bold"
})
]
})