Calculate the longest path through your project network to identify critical tasks.
Project Task Durations
Enter each task and its estimated duration. Tasks can have dependencies on other tasks.
Critical Path Duration: — days
Critical Tasks: —
Understanding the Critical Path
The Critical Path Method (CPM) is a project management technique used to identify a project's critical path. The critical path is the sequence of project activities that determine the shortest possible time to complete the project. Any delay in a critical path task will directly delay the entire project completion date. Tasks not on the critical path have some amount of "float" or "slack," meaning they can be delayed by that amount without affecting the project's overall deadline.
How it Works
The calculation involves several steps, typically performed using a network diagram:
Forward Pass: Calculate the Earliest Start (ES) and Earliest Finish (EF) times for each task. ES is the earliest a task can begin, and EF is the earliest it can finish. The EF of a task is its ES plus its duration. The ES of a task is the maximum EF of all its immediate predecessors. For tasks with no predecessors, ES is typically 0.
Backward Pass: Calculate the Latest Finish (LF) and Latest Start (LS) times for each task. LF is the latest a task can finish without delaying the project, and LS is the latest it can start. The LF of a task is the minimum LS of all its immediate successors. For the final tasks in the project, LF is the project's total duration (calculated from the forward pass). The LS of a task is its LF minus its duration.
Calculate Float (Slack): For each task, float is the difference between its LS and ES (or LF and EF). Float = LS - ES or Float = LF - EF.
Identify Critical Path: Tasks with zero float (i.e., Float = 0) are on the critical path. The critical path itself is the sequence of these zero-float tasks from the project's start to its finish.
The Calculator's Approach
This calculator automates the forward and backward pass calculations. It requires you to input each task's name, its estimated duration, and any tasks it directly depends on. The calculator then:
Determines the earliest possible start and finish times for all tasks.
Determines the latest possible start and finish times for all tasks.
Calculates the float for each task.
Identifies tasks with zero float as critical tasks.
The sum of durations of tasks on the critical path (or the latest finish time of the last task on the critical path) represents the minimum project duration.
Use Cases
Project Planning: Essential for understanding project timelines and setting realistic deadlines.
Resource Allocation: Helps prioritize tasks that require immediate attention and resources.
Risk Management: Highlights tasks where delays are most impactful, allowing for contingency planning.
Performance Monitoring: Tracks progress against the critical path to ensure the project stays on schedule.
By understanding and managing your project's critical path, you can significantly improve your chances of completing projects on time and within budget.
var taskCounter = 1;
function addTaskInput() {
taskCounter++;
var tasksContainer = document.getElementById('tasks-container');
var newTaskDiv = document.createElement('div');
newTaskDiv.classList.add('task-input-group');
newTaskDiv.innerHTML = `
`;
tasksContainer.appendChild(newTaskDiv);
}
function calculateCriticalPath() {
var tasks = [];
var taskInputs = document.querySelectorAll('.task-input-group');
// 1. Collect task data
taskInputs.forEach(function(taskDiv, index) {
var taskName = document.getElementById('taskName' + (index + 1)).value.trim();
var duration = parseFloat(document.getElementById('duration' + (index + 1)).value);
var dependenciesStr = document.getElementById('dependencies' + (index + 1)).value.trim();
var dependencies = dependenciesStr ? dependenciesStr.split(',').map(function(dep) { return dep.trim(); }) : [];
if (taskName && !isNaN(duration)) {
tasks.push({
name: taskName,
duration: duration,
dependencies: dependencies,
es: 0, // Earliest Start
ef: 0, // Earliest Finish
ls: Infinity, // Latest Start
lf: Infinity, // Latest Finish
float: 0
});
}
});
// Create a map for quick lookup by task name
var taskMap = {};
tasks.forEach(function(task) {
taskMap[task.name] = task;
});
// Validate dependencies
for (var i = 0; i < tasks.length; i++) {
for (var j = 0; j 0) {
var currentTask = queue.shift();
// Calculate ES based on predecessors' EF
var maxPredEF = 0;
for (var i = 0; i < currentTask.dependencies.length; i++) {
var predTask = taskMap[currentTask.dependencies[i]];
if (predTask) { // Check if predecessor exists
maxPredEF = Math.max(maxPredEF, predTask.ef);
}
}
currentTask.es = maxPredEF;
currentTask.ef = currentTask.es + currentTask.duration;
projectEarliestFinish = Math.max(projectEarliestFinish, currentTask.ef);
// Find successor tasks and add them to queue if all their dependencies are met
tasks.forEach(function(nextTask) {
if (nextTask.dependencies.includes(currentTask.name)) {
// Check if all dependencies of nextTask are processed
var allDepsMet = true;
for (var k = 0; k t.name === nextTask.dependencies[k])) {
// If a dependency hasn't finished or doesn't exist (and it's meant to exist)
// This check needs refinement for complex graphs and circular dependencies
// For simplicity, we assume valid DAGs and rely on the initial validation.
// A more robust approach would track processed dependencies.
}
}
// Add to queue if not already processed and all deps are notionally met for this iteration
// A better queue management would be needed for complex graphs, maybe topological sort
// For now, we'll rely on repeated passes or a simpler assumption of DAG
queue.push(nextTask); // This simplified approach might re-queue, need to manage properly
}
});
// A more robust way to manage the queue and avoid infinite loops in complex graphs:
// Maintain a count of unmet dependencies for each task.
// When a task finishes, decrement the count for its successors.
// Add successors to the queue when their count reaches zero.
// For this example, we'll use a simplified iterative approach.
}
// Re-run forward pass iteratively until no changes occur to handle complex dependencies
var changed = true;
while(changed) {
changed = false;
tasks.forEach(function(task) {
var maxPredEF = 0;
task.dependencies.forEach(function(depName) {
if (taskMap[depName]) {
maxPredEF = Math.max(maxPredEF, taskMap[depName].ef);
}
});
var newES = maxPredEF;
var newEF = newES + task.duration;
if (newES !== task.es || newEF !== task.ef) {
task.es = newES;
task.ef = newEF;
projectEarliestFinish = Math.max(projectEarliestFinish, task.ef);
changed = true;
}
});
}
// — Backward Pass —
// Initialize LF for tasks with no successors (or assume project finish time)
tasks.forEach(function(task) {
// Find successors
var successors = tasks.filter(function(t) { return t.dependencies.includes(task.name); });
if (successors.length === 0) {
task.lf = projectEarliestFinish; // Set LF to project completion time
} else {
task.lf = Infinity; // Will be updated by successors
}
task.ls = task.lf – task.duration;
});
// Iteratively calculate LF/LS backwards
changed = true;
while(changed) {
changed = false;
// Iterate in reverse order of tasks (or use a topological sort if available)
// For simplicity, we iterate multiple times until values stabilize
tasks.forEach(function(task) {
var successors = tasks.filter(function(t) { return t.dependencies.includes(task.name); });
var minSuccLS = Infinity;
if (successors.length > 0) {
successors.forEach(function(succTask) {
minSuccLS = Math.min(minSuccLS, succTask.ls);
});
} else {
minSuccLS = projectEarliestFinish; // If no successors, use project finish time
}
var newLF = minSuccLS;
var newLS = newLF – task.duration;
// Check if LF/LS are still Infinity before updating (important for initial tasks)
if (task.lf === Infinity) task.lf = newLF; // Assign initial LF if it's infinity
if (task.ls === Infinity) task.ls = newLS; // Assign initial LS if it's infinity
// Update if values change
if (newLF 0) {
successors.forEach(function(succTask) { minSuccLS = Math.min(minSuccLS, succTask.ls); });
} else {
minSuccLS = projectEarliestFinish;
}
task.lf = minSuccLS;
task.ls = task.lf – task.duration;
}
});
// — Calculate Float and Identify Critical Path —
var criticalTasks = [];
tasks.forEach(function(task) {
task.float = task.ls – task.es;
if (task.float === 0) {
criticalTasks.push(task.name);
}
});
criticalTasks.sort(); // Sort for consistent output
// — Display Results —
document.getElementById('criticalPathDuration').textContent = projectEarliestFinish;
document.getElementById('criticalTasks').textContent = criticalTasks.join(', ');
}