diff --git a/src/groovylight/geom.py b/src/groovylight/geom.py
new file mode 100644
index 0000000..dcf384d
--- /dev/null
+++ b/src/groovylight/geom.py
@@ -0,0 +1,124 @@
+# Geometry-related classes and functions. Manipulations and
+# generation of panel-layout metadata for use in the HDL.
+
+from enum import Enum
+from dataclasses import dataclass
+
+
+@dataclass(frozen=True, order=True)
+class Coord:
+    """Coordinate class. Uses computer-graphics standard coordinate system,
+    where X=0, Y=0 is top left. +X goes right. +Y goes down.
+    """
+
+    x: int
+    y: int
+    
+    def __post_init__(self):
+        if self.x < 0 or self.y < 0:
+            raise RuntimeError("x and y must both be >= 0")
+
+
+@dataclass(frozen=True)
+class BBox:
+    """Bounding box class. Captures the top left coordinate and bottom right coordinate
+    of an object"""
+
+    topleft: Coord
+    bottomright: Coord
+
+    def __post_init__(self):
+        if not self.topleft < self.bottomright:
+            raise RuntimeError("topleft must be strictly less than bottomright")
+
+    def contains(self, c: Coord) -> bool:
+        return c > self.topleft and c < self.bottomright
+
+
+@dataclass(frozen=True)
+class DisplayDimensions:
+    """Represents the dimensions of a display string, in length x height. Notably
+    this is in local coordinates to the display. The display top left is 0,0.
+    Uses length/height notation to separate it from coord
+    """
+
+    length: int
+    height: int
+
+
+class DisplayRotation(Enum):
+    """Display rotation enums. The names indicate the general direction
+    of data flow. most normal displays (LCDs, CRTs) are LEFTRIGHT,
+    since they scan left-to-right, top to bottom.
+
+    Note that the direction of subsequent lines is not always clear.
+    They are enumerated below:
+        LEFTRIGHT -> next line is below it (-Y)
+        UPDOWN -> next line is to the LEFT (-X)
+        DOWNUP -> next line is to the RIGHT (+X)
+        RIGHTLEFT -> next line is above it (+Y)
+
+    Generally, prefer LEFTRIGHT/UPDOWN over other rotations.
+    """
+
+    LEFTRIGHT = 0
+    UPDOWN = 1
+    DOWNUP = 2
+    RIGHTLEFT = 3  # why are you like this.
+
+
+@dataclass(frozen=True)
+class _DisplayString:
+    """Internal class to represent a string of HUB75 displays.
+
+    position: (X,Y) coordinates of the local top-left of the display
+
+    dimensions: (length, height) local-coordinate dimensions of the display.
+
+    rotation: DisplayRotation: the orientation of the display.
+    """
+
+    position: Coord
+    dimensions: DisplayDimensions
+    rotation: DisplayRotation
+
+    @property
+    def bbox(self) -> BBox:
+        """Returns the bounding box of the display based on the dimensions, position, and rotation."""
+        x = self.position.x
+        y = self.position.y
+        l = self.dimensions.length
+        h = self.dimensions.height
+        match self.rotation:
+            case DisplayRotation.LEFTRIGHT:
+                return BBox(Coord(x, y), Coord(x + l, y + h))
+            case DisplayRotation.UPDOWN:
+                return BBox(Coord(x - h, y), Coord(x, y + l))
+            case DisplayRotation.DOWNUP:
+                return BBox(Coord(x, y + l), Coord(x + h, y))
+            case DisplayRotation.RIGHTLEFT:
+                return BBox(Coord(x - l, y - h), Coord(x, y))
+
+    def contains_pix(self, coord: Coord) -> bool:
+        """Checks if the given coordinate is inside this display."""
+        return self.bbox.contains(coord)
+
+
+class DisplayGeometry:
+    """Represents a display based on several strings in different positions.
+    """
+
+    def __init__(self, *, strict: bool = False):
+        self.strict = strict
+        pass
+
+    def add_string(self, position: (int, int), rot: int, dimensions: (int, int)):
+        """Add a new string to the display. This new string is located at
+        a specific point, and has a direction, along with dimension that reveal
+        the number of address lines (typically 64, with 1:32 selection so 5 address
+        bits) and the total length of the string which is used to size the line
+        buffers.
+
+        When in strict mode, this method may throw an exception if this new string
+        will overlap with an existing string.
+        """