For two distinct points \(\mathbf A=(A_x,A_y)\) and \(\mathbf B=(B_x,B_y)\), the directed line segment has direction
\[ \mathbf v=\mathbf B-\mathbf A=(B_x-A_x,\,B_y-A_y). \]Rotating \(\mathbf v\) counterclockwise by \(90^\circ\) applies the 2D perp operator:
\[ \mathbf n_\text{left}=\mathbf v^\perp=(-v_y,v_x). \]The opposite vector is the right-hand normal:
\[ \mathbf n_\text{right}=-\mathbf n_\text{left}=(v_y,-v_x). \]Both vectors have the same length as the segment direction. Divide them by \(\lVert\mathbf v\rVert\) when unit normals are required.
JavaScript Implementation
function lineSegmentNormals(A, B, unitLength = false) {
const dx = B.x - A.x;
const dy = B.y - A.y;
const length = Math.hypot(dx, dy);
if (length === 0) return null;
const scale = unitLength ? 1 / length : 1;
return [
{x: -dy * scale, y: dx * scale},
{x: dy * scale, y: -dx * scale}
];
} The first result points to the left of the directed segment \(A\to B\); the second points to the right. Reversing the endpoints swaps these two directions. Equal endpoints return null because a zero-length segment has no direction and therefore no defined normal.
const A = {x: 1, y: 2};
const B = {x: 4, y: 6};
lineSegmentNormals(A, B);
// [{x: -4, y: 3}, {x: 4, y: -3}]
lineSegmentNormals(A, B, true);
// [{x: -0.8, y: 0.6}, {x: 0.8, y: -0.6}]