diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp
index 17a0282ea..5ba2788fe 100644
--- a/source/build/src/clip.cpp
+++ b/source/build/src/clip.cpp
@@ -742,13 +742,13 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int
             vec2_t const  clipr  = { clipit[hitwall].x2 - clipit[hitwall].x1, clipit[hitwall].y2 - clipit[hitwall].y1 };
             // clamp to the max value we can utilize without reworking the scaling below
             // this works around the overflow issue that affects dukedc2.map
-            int32_t const templl = (int32_t)clamp(compat_maybe_truncate_to_int32((int64_t)clipr.x * clipr.x + (int64_t)clipr.y * clipr.y), INT32_MIN, INT32_MAX);
+            int32_t const templl = (int32_t)clamp<int64_t>(compat_maybe_truncate_to_int32((int64_t)clipr.x * clipr.x + (int64_t)clipr.y * clipr.y), INT32_MIN, INT32_MAX);
 
             if (templl > 0)
             {
                 // I don't know if this one actually overflows or not, but I highly doubt it hurts to check
                 int32_t const templl2
-                = (int32_t)clamp(compat_maybe_truncate_to_int32((int64_t)(goal.x - vec.x) * clipr.x + (int64_t)(goal.y - vec.y) * clipr.y), INT32_MIN, INT32_MAX);
+                = (int32_t)clamp<int64_t>(compat_maybe_truncate_to_int32((int64_t)(goal.x - vec.x) * clipr.x + (int64_t)(goal.y - vec.y) * clipr.y), INT32_MIN, INT32_MAX);
                 int32_t const i = (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829 || (abs(templl2)>>11) < templl) ?
                     (int)DivScaleL(templl2, templl, 20) : 0;
 
diff --git a/source/common/engine/renderstyle.cpp b/source/common/engine/renderstyle.cpp
index 1a29292ea..e31538b4f 100644
--- a/source/common/engine/renderstyle.cpp
+++ b/source/common/engine/renderstyle.cpp
@@ -32,7 +32,7 @@
 **
 */
 
-#include "templates.h"
+#include "basics.h"
 #include "renderstyle.h"
 #include "c_cvars.h"
 
diff --git a/source/common/textures/hw_ihwtexture.cpp b/source/common/textures/hw_ihwtexture.cpp
index aebf387d4..f86daa5c1 100644
--- a/source/common/textures/hw_ihwtexture.cpp
+++ b/source/common/textures/hw_ihwtexture.cpp
@@ -34,7 +34,7 @@
 */
 
 #include "hw_ihwtexture.h"
-#include "templates.h"
+#include "basics.h"
 #include "tarray.h"
 #include "xs_Float.h"
 
@@ -68,7 +68,7 @@ static void ResampleBoxPrecalc(TArray<BoxPrecalc>& boxes, int oldDim)
 
 		BoxPrecalc& precalc = boxes[dst];
 		precalc.boxStart = clamp<int>(int(src_p - scale_factor_1 / 2.0 + 1), 0, oldDim - 1);
-		precalc.boxEnd = clamp<int>(MAX<int>(precalc.boxStart + 1, int(src_p + scale_factor_2)), 0, oldDim - 1);
+		precalc.boxEnd = clamp<int>(max<int>(precalc.boxStart + 1, int(src_p + scale_factor_2)), 0, oldDim - 1);
 	}
 }
 
diff --git a/source/common/utility/basics.h b/source/common/utility/basics.h
index 703a84c30..29ee19593 100644
--- a/source/common/utility/basics.h
+++ b/source/common/utility/basics.h
@@ -105,3 +105,4 @@ enum EStateUseFlags
 
 using std::min;
 using std::max;
+using std::clamp;
diff --git a/source/common/utility/templates.h b/source/common/utility/templates.h
index 3f3e596f0..7babf3f99 100644
--- a/source/common/utility/templates.h
+++ b/source/common/utility/templates.h
@@ -129,18 +129,5 @@ const T MAX (const T a, const T b)
 	return a > b ? a : b;
 }
 
-//==========================================================================
-//
-// clamp
-//
-// Clamps in to the range [min,max].
-//==========================================================================
-
-template<typename T, typename X, typename Y>
-inline constexpr
-T clamp (const T in, const X min, const Y max)
-{
-	return in <= (T) min ? (T) min : in >= (T) max ? (T) max : in;
-}
 
 #endif //__TEMPLATES_H__
diff --git a/source/core/binaryangle.h b/source/core/binaryangle.h
index 456790560..bd527bde9 100644
--- a/source/core/binaryangle.h
+++ b/source/core/binaryangle.h
@@ -37,6 +37,7 @@
 #pragma once
 
 #include <math.h>
+#include "basics.h"
 #include "m_fixed.h"
 #include "xs_Float.h"	// needed for reliably overflowing float->int conversions.
 #include "serializer.h"
@@ -225,7 +226,7 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, binangle &obj,
 inline double HorizToPitch(double horiz) { return atan2(horiz, 128) * (180. / pi::pi()); }
 inline double HorizToPitch(fixed_t q16horiz) { return atan2(q16horiz, IntToFixed(128)) * (180. / pi::pi()); }
 inline fixed_t PitchToHoriz(double pitch) { return xs_CRoundToInt(IntToFixed(128) * tan(pitch * (pi::pi() / 180.))); }
-inline int32_t PitchToBAM(double pitch) { return xs_CRoundToInt(clamp(pitch * (1073741823.5 / 45.), -INT32_MAX, INT32_MAX)); }
+inline int32_t PitchToBAM(double pitch) { return xs_CRoundToInt(clamp<double>(pitch * (1073741823.5 / 45.), -INT32_MAX, INT32_MAX)); }
 inline constexpr double BAMToPitch(int32_t bam) { return bam * (45. / 1073741823.5); }
 
 
diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp
index 4a3413bda..12b1ee3a8 100644
--- a/source/games/duke/src/input.cpp
+++ b/source/games/duke/src/input.cpp
@@ -755,7 +755,7 @@ static void processVehicleInput(player_struct *p, ControlInfo* const hidInput, I
 		input.avel = (float)boatApplyTurn(p, hidInput, kbdLeft, kbdRight, scaleAdjust);
 	}
 
-	loc.fvel = clamp(xs_CRoundToInt(p->MotoSpeed), -(MAXVELMOTO >> 3), MAXVELMOTO);
+	loc.fvel = (int16_t)clamp<int>(xs_CRoundToInt(p->MotoSpeed), -(MAXVELMOTO >> 3), MAXVELMOTO);
 	input.avel *= BAngToDegree;
 	loc.avel += input.avel;
 }
diff --git a/source/games/duke/src/sounds.cpp b/source/games/duke/src/sounds.cpp
index 9f886749d..fbc58059c 100644
--- a/source/games/duke/src/sounds.cpp
+++ b/source/games/duke/src/sounds.cpp
@@ -244,10 +244,10 @@ int S_DefineSound(unsigned index, const char *filename, int minpitch, int maxpit
 		fn.Substitute(".ogg", ".voc");
 		sfx->lumpnum = S_LookupSound(fn);
 	}
-	sndinf[kPitchStart] = clamp(minpitch, INT16_MIN, INT16_MAX);
-	sndinf[kPitchEnd] = clamp(maxpitch, INT16_MIN, INT16_MAX);
+	sndinf[kPitchStart] = clamp<int>(minpitch, INT16_MIN, INT16_MAX);
+	sndinf[kPitchEnd] = clamp<int>(maxpitch, INT16_MIN, INT16_MAX);
 	sndinf[kPriority] = priority & 255;
-	sndinf[kVolAdjust] = clamp(distance, INT16_MIN, INT16_MAX);
+	sndinf[kVolAdjust] = clamp<int>(distance, INT16_MIN, INT16_MAX);
 	sndinf[kWorldTourMapping] = 0;
 	sfx->Volume = volume;
 	sfx->NearLimit = 6;
diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp
index 03ed1632a..d7f79bf24 100644
--- a/source/games/exhumed/src/player.cpp
+++ b/source/games/exhumed/src/player.cpp
@@ -2456,7 +2456,7 @@ sectdone:
             double nVertPan = (pPlayer->nDestVertPan - pPlayer->horizon.horiz).asbuildf() * 0.25;
             if (nVertPan != 0)
             {
-                pPlayer->horizon.addadjustment(abs(nVertPan) >= 4 ? clamp(nVertPan, -4, 4) : nVertPan * 2.);
+                pPlayer->horizon.addadjustment(abs(nVertPan) >= 4 ? clamp(nVertPan, -4., 4.) : nVertPan * 2.);
             }
         }
     }
diff --git a/source/games/sw/src/jsector.cpp b/source/games/sw/src/jsector.cpp
index db3f69d6d..1fc9fbcbb 100644
--- a/source/games/sw/src/jsector.cpp
+++ b/source/games/sw/src/jsector.cpp
@@ -695,7 +695,7 @@ void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz, double smoothratio)
                     // 100!
                     if (SP_TAG7(sp) != 0)
                     {
-                        camhoriz = clamp(SP_TAG7(sp), gi->playerHorizMin(), gi->playerHorizMax());
+                        camhoriz = clamp<int>(SP_TAG7(sp), gi->playerHorizMin(), gi->playerHorizMax());
                     }
                     else
                         camhoriz = 0;     // Default