diff --git a/elm.json b/elm.json
index ac7524b7b39a9a5d8f1776abbaf7fb0092d00fc8..b833272986cbd24784dda6392e9b9eddb0d1cbed 100644
--- a/elm.json
+++ b/elm.json
@@ -11,6 +11,7 @@
             "elm/html": "1.0.0",
             "elm/http": "2.0.0",
             "elm/json": "1.1.3",
+            "elm/random": "1.0.0",
             "elm/svg": "1.0.1",
             "elm/url": "1.0.0"
         },
diff --git a/public/index.html b/public/index.html
index 2aa3992dbb269b774c9a915ae0891ef68aafc498..a29effb56bc3d5e660ab67c526f478b65bca30dc 100644
--- a/public/index.html
+++ b/public/index.html
@@ -37,6 +37,33 @@
       .dropdown.is-fullwidth * {
         width: 100%;
       }
+      
+      /* https://stackoverflow.com/a/46550527 */
+      .square-container {
+        display: flex;
+        flex-wrap: wrap;
+      }
+      .square {
+        position: relative;
+        flex-basis: calc(10% - 10px);
+        margin: 5px;
+        box-sizing: border-box;
+      }
+      .square::before {
+        content: '';
+        display: block;
+        padding-top: 100%;
+      }
+      .square .content {
+        position: absolute;
+        top: 0; left: 0;
+        height: 100%;
+        width: 100%;
+
+        display: flex;               /* added for centered text */
+        justify-content: center;     /* added for centered text */
+        align-items: center;         /* added for centered text */
+      }
     </style>
   </head>
   <body>
diff --git a/public/main.js b/public/main.js
index 3e272611a35ebacc8b2bdcb6a445d28183644e90..70db4ccd5288d810c48ed0d0adb10089825af812 100644
--- a/public/main.js
+++ b/public/main.js
@@ -4545,7 +4545,90 @@ function _Http_track(router, xhr, tracker)
 			size: event.lengthComputable ? $elm$core$Maybe$Just(event.total) : $elm$core$Maybe$Nothing
 		}))));
 	});
-}var $author$project$Main$LinkClicked = function (a) {
+}
+
+
+var _Bitwise_and = F2(function(a, b)
+{
+	return a & b;
+});
+
+var _Bitwise_or = F2(function(a, b)
+{
+	return a | b;
+});
+
+var _Bitwise_xor = F2(function(a, b)
+{
+	return a ^ b;
+});
+
+function _Bitwise_complement(a)
+{
+	return ~a;
+};
+
+var _Bitwise_shiftLeftBy = F2(function(offset, a)
+{
+	return a << offset;
+});
+
+var _Bitwise_shiftRightBy = F2(function(offset, a)
+{
+	return a >> offset;
+});
+
+var _Bitwise_shiftRightZfBy = F2(function(offset, a)
+{
+	return a >>> offset;
+});
+
+
+
+function _Time_now(millisToPosix)
+{
+	return _Scheduler_binding(function(callback)
+	{
+		callback(_Scheduler_succeed(millisToPosix(Date.now())));
+	});
+}
+
+var _Time_setInterval = F2(function(interval, task)
+{
+	return _Scheduler_binding(function(callback)
+	{
+		var id = setInterval(function() { _Scheduler_rawSpawn(task); }, interval);
+		return function() { clearInterval(id); };
+	});
+});
+
+function _Time_here()
+{
+	return _Scheduler_binding(function(callback)
+	{
+		callback(_Scheduler_succeed(
+			A2($elm$time$Time$customZone, -(new Date().getTimezoneOffset()), _List_Nil)
+		));
+	});
+}
+
+
+function _Time_getZoneName()
+{
+	return _Scheduler_binding(function(callback)
+	{
+		try
+		{
+			var name = $elm$time$Time$Name(Intl.DateTimeFormat().resolvedOptions().timeZone);
+		}
+		catch (e)
+		{
+			var name = $elm$time$Time$Offset(new Date().getTimezoneOffset());
+		}
+		callback(_Scheduler_succeed(name));
+	});
+}
+var $author$project$Main$LinkClicked = function (a) {
 	return {$: 'LinkClicked', a: a};
 };
 var $author$project$Main$UrlChanged = function (a) {
@@ -5512,12 +5595,13 @@ var $author$project$Stats$new = _List_fromArray(
 		A2($author$project$Stats$newBackground, 'Wonder', 5)
 	]);
 var $author$project$Character$new = {creationPoints: $author$project$Creation$new, name: 'Default Name', stats: $author$project$Stats$new};
+var $author$project$Dice$new = {amount: 1, difficulty: 6, specialty: false, willpower: false};
 var $elm$core$Platform$Cmd$batch = _Platform_batch;
 var $elm$core$Platform$Cmd$none = $elm$core$Platform$Cmd$batch(_List_Nil);
 var $author$project$Main$init = F3(
 	function (_v0, url, key) {
 		return _Utils_Tuple2(
-			{activeDropdown: $elm$core$Maybe$Nothing, character: $author$project$Character$new, key: key, modal: $elm$core$Maybe$Nothing, modalValue: '', url: url},
+			{activeDropdown: $elm$core$Maybe$Nothing, character: $author$project$Character$new, dice: $author$project$Dice$new, key: key, modal: $elm$core$Maybe$Nothing, modalValue: '', roll: _List_Nil, url: url},
 			$elm$core$Platform$Cmd$none);
 	});
 var $elm$core$Platform$Sub$batch = _Platform_batch;
@@ -6769,6 +6853,194 @@ var $author$project$Main$modalValue = F2(
 		return character.name;
 	});
 var $elm$browser$Browser$Navigation$pushUrl = _Browser_pushUrl;
+var $author$project$Main$RollResult = function (a) {
+	return {$: 'RollResult', a: a};
+};
+var $elm$random$Random$Generate = function (a) {
+	return {$: 'Generate', a: a};
+};
+var $elm$random$Random$Seed = F2(
+	function (a, b) {
+		return {$: 'Seed', a: a, b: b};
+	});
+var $elm$core$Bitwise$shiftRightZfBy = _Bitwise_shiftRightZfBy;
+var $elm$random$Random$next = function (_v0) {
+	var state0 = _v0.a;
+	var incr = _v0.b;
+	return A2($elm$random$Random$Seed, ((state0 * 1664525) + incr) >>> 0, incr);
+};
+var $elm$random$Random$initialSeed = function (x) {
+	var _v0 = $elm$random$Random$next(
+		A2($elm$random$Random$Seed, 0, 1013904223));
+	var state1 = _v0.a;
+	var incr = _v0.b;
+	var state2 = (state1 + x) >>> 0;
+	return $elm$random$Random$next(
+		A2($elm$random$Random$Seed, state2, incr));
+};
+var $elm$time$Time$Name = function (a) {
+	return {$: 'Name', a: a};
+};
+var $elm$time$Time$Offset = function (a) {
+	return {$: 'Offset', a: a};
+};
+var $elm$time$Time$Zone = F2(
+	function (a, b) {
+		return {$: 'Zone', a: a, b: b};
+	});
+var $elm$time$Time$customZone = $elm$time$Time$Zone;
+var $elm$time$Time$Posix = function (a) {
+	return {$: 'Posix', a: a};
+};
+var $elm$time$Time$millisToPosix = $elm$time$Time$Posix;
+var $elm$time$Time$now = _Time_now($elm$time$Time$millisToPosix);
+var $elm$time$Time$posixToMillis = function (_v0) {
+	var millis = _v0.a;
+	return millis;
+};
+var $elm$random$Random$init = A2(
+	$elm$core$Task$andThen,
+	function (time) {
+		return $elm$core$Task$succeed(
+			$elm$random$Random$initialSeed(
+				$elm$time$Time$posixToMillis(time)));
+	},
+	$elm$time$Time$now);
+var $elm$random$Random$step = F2(
+	function (_v0, seed) {
+		var generator = _v0.a;
+		return generator(seed);
+	});
+var $elm$random$Random$onEffects = F3(
+	function (router, commands, seed) {
+		if (!commands.b) {
+			return $elm$core$Task$succeed(seed);
+		} else {
+			var generator = commands.a.a;
+			var rest = commands.b;
+			var _v1 = A2($elm$random$Random$step, generator, seed);
+			var value = _v1.a;
+			var newSeed = _v1.b;
+			return A2(
+				$elm$core$Task$andThen,
+				function (_v2) {
+					return A3($elm$random$Random$onEffects, router, rest, newSeed);
+				},
+				A2($elm$core$Platform$sendToApp, router, value));
+		}
+	});
+var $elm$random$Random$onSelfMsg = F3(
+	function (_v0, _v1, seed) {
+		return $elm$core$Task$succeed(seed);
+	});
+var $elm$random$Random$Generator = function (a) {
+	return {$: 'Generator', a: a};
+};
+var $elm$random$Random$map = F2(
+	function (func, _v0) {
+		var genA = _v0.a;
+		return $elm$random$Random$Generator(
+			function (seed0) {
+				var _v1 = genA(seed0);
+				var a = _v1.a;
+				var seed1 = _v1.b;
+				return _Utils_Tuple2(
+					func(a),
+					seed1);
+			});
+	});
+var $elm$random$Random$cmdMap = F2(
+	function (func, _v0) {
+		var generator = _v0.a;
+		return $elm$random$Random$Generate(
+			A2($elm$random$Random$map, func, generator));
+	});
+_Platform_effectManagers['Random'] = _Platform_createManager($elm$random$Random$init, $elm$random$Random$onEffects, $elm$random$Random$onSelfMsg, $elm$random$Random$cmdMap);
+var $elm$random$Random$command = _Platform_leaf('Random');
+var $elm$random$Random$generate = F2(
+	function (tagger, generator) {
+		return $elm$random$Random$command(
+			$elm$random$Random$Generate(
+				A2($elm$random$Random$map, tagger, generator)));
+	});
+var $elm$core$Bitwise$and = _Bitwise_and;
+var $elm$core$Bitwise$xor = _Bitwise_xor;
+var $elm$random$Random$peel = function (_v0) {
+	var state = _v0.a;
+	var word = (state ^ (state >>> ((state >>> 28) + 4))) * 277803737;
+	return ((word >>> 22) ^ word) >>> 0;
+};
+var $elm$random$Random$int = F2(
+	function (a, b) {
+		return $elm$random$Random$Generator(
+			function (seed0) {
+				var _v0 = (_Utils_cmp(a, b) < 0) ? _Utils_Tuple2(a, b) : _Utils_Tuple2(b, a);
+				var lo = _v0.a;
+				var hi = _v0.b;
+				var range = (hi - lo) + 1;
+				if (!((range - 1) & range)) {
+					return _Utils_Tuple2(
+						(((range - 1) & $elm$random$Random$peel(seed0)) >>> 0) + lo,
+						$elm$random$Random$next(seed0));
+				} else {
+					var threshhold = (((-range) >>> 0) % range) >>> 0;
+					var accountForBias = function (seed) {
+						accountForBias:
+						while (true) {
+							var x = $elm$random$Random$peel(seed);
+							var seedN = $elm$random$Random$next(seed);
+							if (_Utils_cmp(x, threshhold) < 0) {
+								var $temp$seed = seedN;
+								seed = $temp$seed;
+								continue accountForBias;
+							} else {
+								return _Utils_Tuple2((x % range) + lo, seedN);
+							}
+						}
+					};
+					return accountForBias(seed0);
+				}
+			});
+	});
+var $elm$random$Random$listHelp = F4(
+	function (revList, n, gen, seed) {
+		listHelp:
+		while (true) {
+			if (n < 1) {
+				return _Utils_Tuple2(revList, seed);
+			} else {
+				var _v0 = gen(seed);
+				var value = _v0.a;
+				var newSeed = _v0.b;
+				var $temp$revList = A2($elm$core$List$cons, value, revList),
+					$temp$n = n - 1,
+					$temp$gen = gen,
+					$temp$seed = newSeed;
+				revList = $temp$revList;
+				n = $temp$n;
+				gen = $temp$gen;
+				seed = $temp$seed;
+				continue listHelp;
+			}
+		}
+	});
+var $elm$random$Random$list = F2(
+	function (n, _v0) {
+		var gen = _v0.a;
+		return $elm$random$Random$Generator(
+			function (seed) {
+				return A4($elm$random$Random$listHelp, _List_Nil, n, gen, seed);
+			});
+	});
+var $author$project$Main$roll = function (dice) {
+	return A2(
+		$elm$random$Random$generate,
+		$author$project$Main$RollResult,
+		A2(
+			$elm$random$Random$list,
+			dice.amount,
+			A2($elm$random$Random$int, 1, 10)));
+};
 var $author$project$Stats$setAffinityIfSame = F2(
 	function (sphere, stat) {
 		var _v0 = stat.statType;
@@ -6816,6 +7088,9 @@ var $author$project$Character$setAffinity = F2(
 				stats: A2($author$project$Stats$setAffinity, character.stats, stat)
 			});
 	});
+var $elm$core$List$sort = function (xs) {
+	return A2($elm$core$List$sortBy, $elm$core$Basics$identity, xs);
+};
 var $elm$url$Url$addPort = F2(
 	function (maybePort, starter) {
 		if (maybePort.$ === 'Nothing') {
@@ -6985,16 +7260,44 @@ var $author$project$Main$update = F2(
 						model,
 						{url: url}),
 					$elm$core$Platform$Cmd$none);
-			default:
+			case 'ResetCharacter':
 				return _Utils_Tuple2(
 					_Utils_update(
 						model,
 						{character: $author$project$Character$new}),
 					$elm$core$Platform$Cmd$none);
+			case 'SetDice':
+				var dice = msg.a;
+				return _Utils_Tuple2(
+					_Utils_update(
+						model,
+						{dice: dice}),
+					$elm$core$Platform$Cmd$none);
+			case 'Roll':
+				return _Utils_Tuple2(
+					model,
+					$author$project$Main$roll(model.dice));
+			case 'RollResult':
+				var result = msg.a;
+				return _Utils_Tuple2(
+					_Utils_update(
+						model,
+						{
+							roll: $elm$core$List$reverse(
+								$elm$core$List$sort(result))
+						}),
+					$elm$core$Platform$Cmd$none);
+			default:
+				return _Utils_Tuple2(
+					_Utils_update(
+						model,
+						{dice: $author$project$Dice$new, roll: _List_Nil}),
+					$elm$core$Platform$Cmd$none);
 		}
 	});
 var $elm$html$Html$div = _VirtualDom_node('div');
 var $author$project$Main$CreateCharacter = {$: 'CreateCharacter'};
+var $author$project$Main$RollDice = {$: 'RollDice'};
 var $author$project$Main$ViewCharacter = {$: 'ViewCharacter'};
 var $author$project$Main$getMode = function (url) {
 	var _v0 = url.fragment;
@@ -7005,6 +7308,8 @@ var $author$project$Main$getMode = function (url) {
 				return $author$project$Main$ViewCharacter;
 			case 'create':
 				return $author$project$Main$CreateCharacter;
+			case 'dice':
+				return $author$project$Main$RollDice;
 			default:
 				return $author$project$Main$CreateCharacter;
 		}
@@ -8363,6 +8668,444 @@ var $author$project$Main$viewCreate = function (model) {
 				$author$project$Main$viewModal(model)
 			]));
 };
+var $author$project$Main$Roll = {$: 'Roll'};
+var $author$project$Main$SetDice = function (a) {
+	return {$: 'SetDice', a: a};
+};
+var $author$project$Dice$changeAmountTo = F2(
+	function (dice, amount) {
+		return _Utils_update(
+			dice,
+			{amount: amount});
+	});
+var $author$project$Dice$changeDifficultyTo = F2(
+	function (dice, difficulty) {
+		return _Utils_update(
+			dice,
+			{
+				difficulty: (difficulty > 10) ? 10 : difficulty
+			});
+	});
+var $elm$html$Html$label = _VirtualDom_node('label');
+var $elm$html$Html$Attributes$max = $elm$html$Html$Attributes$stringProperty('max');
+var $elm$html$Html$Attributes$min = $elm$html$Html$Attributes$stringProperty('min');
+var $elm$core$Basics$not = _Basics_not;
+var $author$project$Dice$switchSpecialty = function (dice) {
+	return _Utils_update(
+		dice,
+		{specialty: !dice.specialty});
+};
+var $author$project$Dice$switchWillpower = function (dice) {
+	return _Utils_update(
+		dice,
+		{willpower: !dice.willpower});
+};
+var $author$project$Main$viewDiceInput = function (model) {
+	return A2(
+		$elm$html$Html$div,
+		_List_Nil,
+		_List_fromArray(
+			[
+				A2(
+				$elm$html$Html$div,
+				_List_fromArray(
+					[
+						$elm$html$Html$Attributes$class('field is-horizontal')
+					]),
+				_List_fromArray(
+					[
+						A2(
+						$elm$html$Html$div,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('field-label is-normal')
+							]),
+						_List_fromArray(
+							[
+								A2(
+								$elm$html$Html$div,
+								_List_fromArray(
+									[
+										$elm$html$Html$Attributes$class('label')
+									]),
+								_List_fromArray(
+									[
+										$elm$html$Html$text('Amount')
+									]))
+							])),
+						A2(
+						$elm$html$Html$div,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('field-body')
+							]),
+						_List_fromArray(
+							[
+								A2(
+								$elm$html$Html$div,
+								_List_fromArray(
+									[
+										$elm$html$Html$Attributes$class('field')
+									]),
+								_List_fromArray(
+									[
+										A2(
+										$elm$html$Html$div,
+										_List_fromArray(
+											[
+												$elm$html$Html$Attributes$class('control')
+											]),
+										_List_fromArray(
+											[
+												A2(
+												$elm$html$Html$input,
+												_List_fromArray(
+													[
+														$elm$html$Html$Attributes$class('input'),
+														$elm$html$Html$Attributes$type_('number'),
+														$elm$html$Html$Attributes$min('1'),
+														$elm$html$Html$Attributes$placeholder('1'),
+														$elm$html$Html$Events$onInput(
+														function (x) {
+															return $author$project$Main$SetDice(
+																A2(
+																	$author$project$Dice$changeAmountTo,
+																	model.dice,
+																	A2(
+																		$elm$core$Maybe$withDefault,
+																		1,
+																		$elm$core$String$toInt(x))));
+														})
+													]),
+												_List_Nil)
+											]))
+									]))
+							]))
+					])),
+				A2(
+				$elm$html$Html$div,
+				_List_fromArray(
+					[
+						$elm$html$Html$Attributes$class('field is-horizontal')
+					]),
+				_List_fromArray(
+					[
+						A2(
+						$elm$html$Html$div,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('field-label is-normal')
+							]),
+						_List_fromArray(
+							[
+								A2(
+								$elm$html$Html$div,
+								_List_fromArray(
+									[
+										$elm$html$Html$Attributes$class('label')
+									]),
+								_List_fromArray(
+									[
+										$elm$html$Html$text('Difficulty')
+									]))
+							])),
+						A2(
+						$elm$html$Html$div,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('field-body')
+							]),
+						_List_fromArray(
+							[
+								A2(
+								$elm$html$Html$div,
+								_List_fromArray(
+									[
+										$elm$html$Html$Attributes$class('field')
+									]),
+								_List_fromArray(
+									[
+										A2(
+										$elm$html$Html$div,
+										_List_fromArray(
+											[
+												$elm$html$Html$Attributes$class('control')
+											]),
+										_List_fromArray(
+											[
+												A2(
+												$elm$html$Html$input,
+												_List_fromArray(
+													[
+														$elm$html$Html$Attributes$class('input'),
+														$elm$html$Html$Attributes$type_('number'),
+														$elm$html$Html$Attributes$min('1'),
+														$elm$html$Html$Attributes$max('10'),
+														$elm$html$Html$Attributes$placeholder('6'),
+														$elm$html$Html$Events$onInput(
+														function (x) {
+															return $author$project$Main$SetDice(
+																A2(
+																	$author$project$Dice$changeDifficultyTo,
+																	model.dice,
+																	A2(
+																		$elm$core$Maybe$withDefault,
+																		6,
+																		$elm$core$String$toInt(x))));
+														})
+													]),
+												_List_Nil)
+											]))
+									]))
+							]))
+					])),
+				A2(
+				$elm$html$Html$div,
+				_List_fromArray(
+					[
+						$elm$html$Html$Attributes$class('field')
+					]),
+				_List_fromArray(
+					[
+						A2(
+						$elm$html$Html$label,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('checkbox')
+							]),
+						_List_fromArray(
+							[
+								A2(
+								$elm$html$Html$input,
+								_List_fromArray(
+									[
+										$elm$html$Html$Attributes$type_('checkbox'),
+										$elm$html$Html$Events$onClick(
+										$author$project$Main$SetDice(
+											$author$project$Dice$switchWillpower(model.dice)))
+									]),
+								_List_Nil),
+								$elm$html$Html$text('Use Willpower')
+							]))
+					])),
+				A2(
+				$elm$html$Html$div,
+				_List_fromArray(
+					[
+						$elm$html$Html$Attributes$class('field')
+					]),
+				_List_fromArray(
+					[
+						A2(
+						$elm$html$Html$label,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('checkbox')
+							]),
+						_List_fromArray(
+							[
+								A2(
+								$elm$html$Html$input,
+								_List_fromArray(
+									[
+										$elm$html$Html$Attributes$type_('checkbox'),
+										$elm$html$Html$Events$onClick(
+										$author$project$Main$SetDice(
+											$author$project$Dice$switchSpecialty(model.dice)))
+									]),
+								_List_Nil),
+								$elm$html$Html$text('Is Specialty')
+							]))
+					])),
+				A2(
+				$elm$html$Html$div,
+				_List_fromArray(
+					[
+						$elm$html$Html$Attributes$class('field is-horizontal')
+					]),
+				_List_fromArray(
+					[
+						A2(
+						$elm$html$Html$div,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('field-body')
+							]),
+						_List_fromArray(
+							[
+								A2(
+								$elm$html$Html$div,
+								_List_fromArray(
+									[
+										$elm$html$Html$Attributes$class('field')
+									]),
+								_List_fromArray(
+									[
+										A2(
+										$elm$html$Html$div,
+										_List_fromArray(
+											[
+												$elm$html$Html$Attributes$class('control')
+											]),
+										_List_fromArray(
+											[
+												A2(
+												$elm$html$Html$button,
+												_List_fromArray(
+													[
+														$elm$html$Html$Attributes$class('button is-primary is-fullwidth'),
+														$elm$html$Html$Events$onClick($author$project$Main$Roll)
+													]),
+												_List_fromArray(
+													[
+														$elm$html$Html$text('Roll Dice')
+													]))
+											]))
+									]))
+							]))
+					]))
+			]));
+};
+var $elm$core$Basics$ge = _Utils_ge;
+var $author$project$Dice$successes = F2(
+	function (dice, roll) {
+		var special = dice.specialty ? $elm$core$List$length(
+			A2(
+				$elm$core$List$filter,
+				function (x) {
+					return x === 10;
+				},
+				roll)) : 0;
+		var plus = $elm$core$List$length(
+			A2(
+				$elm$core$List$filter,
+				function (x) {
+					return _Utils_cmp(x, dice.difficulty) > -1;
+				},
+				roll));
+		var minus = $elm$core$List$length(
+			A2(
+				$elm$core$List$filter,
+				function (x) {
+					return x === 1;
+				},
+				A2(
+					$elm$core$List$filter,
+					function (x) {
+						return _Utils_cmp(x, dice.difficulty) < 0;
+					},
+					roll)));
+		var result = (special + plus) - minus;
+		return dice.willpower ? ((result < 1) ? 1 : result) : result;
+	});
+var $author$project$Main$viewResultDie = F2(
+	function (difficulty, result) {
+		var color = (_Utils_cmp(result, difficulty) > -1) ? ' is-success' : ((result === 1) ? ' is-danger' : '');
+		return A2(
+			$elm$html$Html$div,
+			_List_fromArray(
+				[
+					$elm$html$Html$Attributes$class('square')
+				]),
+			_List_fromArray(
+				[
+					A2(
+					$elm$html$Html$div,
+					_List_fromArray(
+						[
+							$elm$html$Html$Attributes$class('content notification' + color)
+						]),
+					_List_fromArray(
+						[
+							A2(
+							$elm$html$Html$div,
+							_List_fromArray(
+								[
+									$elm$html$Html$Attributes$class('label is-large')
+								]),
+							_List_fromArray(
+								[
+									$elm$html$Html$text(
+									$elm$core$String$fromInt(result))
+								]))
+						]))
+				]));
+	});
+var $author$project$Main$viewSuccesses = function (successes) {
+	var successText = (!successes) ? 'Failure' : ((successes > 0) ? ((successes > 1) ? ($elm$core$String$fromInt(successes) + ' Successes') : '1 Success') : ((_Utils_cmp(successes, -1) < 0) ? ($elm$core$String$fromInt(-successes) + ' Botches') : '1 Botch'));
+	var color = (successes > 0) ? ' is-success' : ' is-danger';
+	return A2(
+		$elm$html$Html$div,
+		_List_fromArray(
+			[
+				$elm$html$Html$Attributes$class('notification' + color)
+			]),
+		_List_fromArray(
+			[
+				$elm$html$Html$text(successText)
+			]));
+};
+var $author$project$Main$viewDiceRoll = F2(
+	function (dice, rollResult) {
+		return ($elm$core$List$length(rollResult) > 0) ? A2(
+			$elm$html$Html$div,
+			_List_Nil,
+			_List_fromArray(
+				[
+					$author$project$Main$viewSuccesses(
+					A2($author$project$Dice$successes, dice, rollResult)),
+					A2(
+					$elm$html$Html$div,
+					_List_fromArray(
+						[
+							$elm$html$Html$Attributes$class('square-container')
+						]),
+					A2(
+						$elm$core$List$map,
+						$author$project$Main$viewResultDie(dice.difficulty),
+						rollResult))
+				])) : A2($elm$html$Html$div, _List_Nil, _List_Nil);
+	});
+var $author$project$Main$viewDice = function (model) {
+	return A2(
+		$elm$html$Html$div,
+		_List_fromArray(
+			[
+				$elm$html$Html$Attributes$class('container')
+			]),
+		_List_fromArray(
+			[
+				A2(
+				$elm$html$Html$div,
+				_List_fromArray(
+					[
+						$elm$html$Html$Attributes$class('columns is-centered')
+					]),
+				_List_fromArray(
+					[
+						A2(
+						$elm$html$Html$div,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('column is-one-third')
+							]),
+						_List_fromArray(
+							[
+								$author$project$Main$viewDiceInput(model)
+							])),
+						A2(
+						$elm$html$Html$div,
+						_List_fromArray(
+							[
+								$elm$html$Html$Attributes$class('column is-two-thirds')
+							]),
+						_List_fromArray(
+							[
+								A2($author$project$Main$viewDiceRoll, model.dice, model.roll)
+							]))
+					]))
+			]));
+};
 var $author$project$Main$ResetCharacter = {$: 'ResetCharacter'};
 var $author$project$Main$ariaExpanded = function (value) {
 	return A2($elm$html$Html$Attributes$attribute, 'aria-expanded', value);
@@ -8448,6 +9191,66 @@ var $author$project$Main$viewImport = function (url) {
 					]))
 			]));
 };
+var $author$project$Main$ResetRoll = {$: 'ResetRoll'};
+var $author$project$Main$viewNavbarDice = A2(
+	$elm$html$Html$div,
+	_List_fromArray(
+		[
+			$elm$html$Html$Attributes$class('navbar-end')
+		]),
+	_List_fromArray(
+		[
+			A2(
+			$elm$html$Html$div,
+			_List_fromArray(
+				[
+					$elm$html$Html$Attributes$class('navbar-item')
+				]),
+			_List_fromArray(
+				[
+					A2(
+					$elm$html$Html$a,
+					_List_fromArray(
+						[
+							$elm$html$Html$Attributes$class('button'),
+							$elm$html$Html$Attributes$href('#dice'),
+							$elm$html$Html$Events$onClick($author$project$Main$ResetRoll)
+						]),
+					_List_fromArray(
+						[
+							$elm$html$Html$text('Roll Dice')
+						]))
+				]))
+		]));
+var $author$project$Main$viewNavbarDiceToChar = A2(
+	$elm$html$Html$div,
+	_List_fromArray(
+		[
+			$elm$html$Html$Attributes$class('navbar-end')
+		]),
+	_List_fromArray(
+		[
+			A2(
+			$elm$html$Html$div,
+			_List_fromArray(
+				[
+					$elm$html$Html$Attributes$class('navbar-item')
+				]),
+			_List_fromArray(
+				[
+					A2(
+					$elm$html$Html$a,
+					_List_fromArray(
+						[
+							$elm$html$Html$Attributes$class('button'),
+							$elm$html$Html$Attributes$href('#view')
+						]),
+					_List_fromArray(
+						[
+							$elm$html$Html$text('Back to Character')
+						]))
+				]))
+		]));
 var $author$project$Main$viewNavbarFinish = A2(
 	$elm$html$Html$div,
 	_List_fromArray(
@@ -8582,10 +9385,13 @@ var $author$project$Main$viewNavbar = function (model) {
 							])),
 						function () {
 						var _v0 = $author$project$Main$getMode(model.url);
-						if (_v0.$ === 'CreateCharacter') {
-							return $author$project$Main$viewNavbarFinish;
-						} else {
-							return A2($elm$html$Html$div, _List_Nil, _List_Nil);
+						switch (_v0.$) {
+							case 'CreateCharacter':
+								return $author$project$Main$viewNavbarFinish;
+							case 'ViewCharacter':
+								return $author$project$Main$viewNavbarDice;
+							default:
+								return $author$project$Main$viewNavbarDiceToChar;
 						}
 					}()
 					]))
@@ -9614,10 +10420,13 @@ var $author$project$Main$view = function (model) {
 						$author$project$Main$viewNavbar(model),
 						function () {
 						var _v0 = $author$project$Main$getMode(model.url);
-						if (_v0.$ === 'ViewCharacter') {
-							return $author$project$Main$viewView(model);
-						} else {
-							return $author$project$Main$viewCreate(model);
+						switch (_v0.$) {
+							case 'ViewCharacter':
+								return $author$project$Main$viewView(model);
+							case 'CreateCharacter':
+								return $author$project$Main$viewCreate(model);
+							default:
+								return $author$project$Main$viewDice(model);
 						}
 					}()
 					]))
diff --git a/src/Dice.elm b/src/Dice.elm
new file mode 100644
index 0000000000000000000000000000000000000000..70c31bdc876d8487ffb7c8bcd209b5e93aed7730
--- /dev/null
+++ b/src/Dice.elm
@@ -0,0 +1,65 @@
+module Dice exposing (..)
+
+type alias Dice =
+    { amount : Int
+    , difficulty : Int
+    , willpower : Bool
+    , specialty : Bool
+    }
+
+new : Dice
+new =
+    { amount = 1
+    , difficulty = 6
+    , willpower = False
+    , specialty = False
+    }
+
+changeAmountTo : Dice -> Int -> Dice
+changeAmountTo dice amount =
+    { dice | amount = amount }
+
+changeDifficultyTo : Dice -> Int -> Dice
+changeDifficultyTo dice difficulty =
+    { dice 
+    | difficulty =
+        if difficulty > 10
+        then 10
+        else difficulty 
+    }
+
+switchWillpower : Dice -> Dice
+switchWillpower dice =
+    { dice | willpower = not dice.willpower }
+
+switchSpecialty : Dice -> Dice
+switchSpecialty dice =
+    { dice | specialty = not dice.specialty }
+
+successes : Dice -> List Int -> Int
+successes dice roll =
+    let
+        special : Int
+        special =
+            if dice.specialty
+            then
+                roll
+                |> List.filter ( \x -> x == 10 )
+                |> List.length
+            else 0
+        plus : Int
+        plus = roll
+            |> List.filter ( \x -> x >= dice.difficulty )
+            |> List.length
+        minus : Int
+        minus = roll
+            |> List.filter ( \x -> x < dice.difficulty )
+            |> List.filter ( \x -> x == 1 )
+            |> List.length
+        result = special + plus - minus
+    in
+        if dice.willpower
+        then if result < 1
+            then 1
+            else result
+        else result
\ No newline at end of file
diff --git a/src/Main.elm b/src/Main.elm
index fb3044818ed0e381e42523c485314eaf813336b1..d0d101081621926dabd35bdc8a0be3cd9675d07c 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -16,8 +16,8 @@ import Http
 import Json.Decode
 import Url
 import Browser.Navigation as Nav
-import Url.Parser exposing (Parser, (</>))
-import Url.Parser.Query
+import Dice exposing (Dice)
+import Random
 
 main : Program () Model Msg
 main = 
@@ -38,6 +38,8 @@ init _ url key =
         , activeDropdown = Nothing
         , key = key
         , url = url
+        , dice = Dice.new
+        , roll = []
         }
     , Cmd.none
     )
@@ -49,6 +51,8 @@ type alias Model =
     , activeDropdown : Maybe ActiveDropdown
     , key : Nav.Key
     , url : Url.Url
+    , dice : Dice
+    , roll : List Int
     }
 
 type ActiveDropdown
@@ -57,6 +61,7 @@ type ActiveDropdown
 type Mode
     = CreateCharacter
     | ViewCharacter
+    | RollDice
 
 type Msg 
     = OpenModal ModalType
@@ -72,6 +77,10 @@ type Msg
     | LinkClicked Browser.UrlRequest
     | UrlChanged Url.Url
     | ResetCharacter
+    | SetDice Dice
+    | Roll
+    | RollResult ( List Int )
+    | ResetRoll
 
 type ModalType
     = Name
@@ -169,6 +178,34 @@ update msg model =
                 }
             , Cmd.none
             )
+        SetDice dice ->
+            (   { model
+                | dice = dice
+                }
+            , Cmd.none
+            )
+        Roll ->
+            ( model
+            , roll model.dice
+            )
+        RollResult result ->
+            (   { model
+                | roll = List.reverse <| List.sort <| result
+                }
+            , Cmd.none
+            )
+        ResetRoll ->
+            (   { model
+                | roll = []
+                , dice = Dice.new
+                }
+            , Cmd.none
+            )
+
+
+roll : Dice -> Cmd Msg
+roll dice =
+    Random.generate RollResult ( Random.list dice.amount ( Random.int 1 10 ) )
 
 getMode : Url.Url -> Mode
 getMode url =
@@ -177,6 +214,7 @@ getMode url =
             case fragment of
                 "view" -> ViewCharacter
                 "create" -> CreateCharacter
+                "dice" -> RollDice
                 _ -> CreateCharacter
         Nothing ->
             CreateCharacter
@@ -194,6 +232,7 @@ view model =
             , case getMode model.url of
                 ViewCharacter -> viewView model
                 CreateCharacter -> viewCreate model
+                RollDice -> viewDice model
             ]
         ]
     }
@@ -235,7 +274,8 @@ viewNavbar model =
                 ]
             , case getMode model.url of
                 CreateCharacter -> viewNavbarFinish
-                ViewCharacter -> div [] []
+                ViewCharacter -> viewNavbarDice
+                RollDice -> viewNavbarDiceToChar
             ]
         ]
 
@@ -251,6 +291,31 @@ viewNavbarFinish =
             ]
         ]
 
+viewNavbarDice : Html Msg
+viewNavbarDice =
+    div [ class "navbar-end" ]
+        [ div [ class "navbar-item" ] 
+            [ a 
+                [ class "button" 
+                , href "#dice"
+                , onClick ResetRoll
+                ] 
+                [ text "Roll Dice" ]
+            ]
+        ]
+
+viewNavbarDiceToChar : Html Msg
+viewNavbarDiceToChar =
+    div [ class "navbar-end" ]
+        [ div [ class "navbar-item" ] 
+            [ a 
+                [ class "button" 
+                , href "#view"
+                ] 
+                [ text "Back to Character" ]
+            ]
+        ]
+
 viewImport : Url.Url -> Html Msg
 viewImport url =
     div [ class "navbar-item has-dropdown is-hoverable" ]
@@ -974,6 +1039,149 @@ vViewAbilities abilities dotSize =
             ]
         ]
 
+viewDice : Model -> Html Msg
+viewDice model =
+    div [ class "container" ] 
+        [ div [ class "columns is-centered" ]
+            [ div [ class "column is-one-third" ]
+                [ viewDiceInput model
+                ]
+            , div [ class "column is-two-thirds" ]
+                [ viewDiceRoll model.dice model.roll
+                ]
+            ]
+        ]
+
+viewDiceInput : Model -> Html Msg
+viewDiceInput model = 
+    div [] 
+        [ div [ class "field is-horizontal" ] 
+            [ div [ class "field-label is-normal" ]
+                [ div [ class "label" ] [ text "Amount" ] ]
+            , div [ class "field-body" ]
+                [ div [ class "field" ]
+                    [ div [ class "control" ] 
+                        [ input 
+                            [ class "input"
+                            , type_ "number"
+                            , Html.Attributes.min "1"
+                            , placeholder "1"
+                            , onInput ( \x -> SetDice ( Dice.changeAmountTo model.dice ( Maybe.withDefault 1 <| String.toInt x ) ) )
+                            ] []
+                        ]
+                    ]
+                ]
+            ]
+        , div [ class "field is-horizontal" ] 
+            [ div [ class "field-label is-normal" ]
+                [ div [ class "label" ] [ text "Difficulty" ] ]
+            , div [ class "field-body" ]
+                [ div [ class "field" ]
+                    [ div [ class "control" ] 
+                        [ input 
+                            [ class "input"
+                            , type_ "number"
+                            , Html.Attributes.min "1"
+                            , Html.Attributes.max "10"
+                            , placeholder "6"
+                            , onInput ( \x -> SetDice ( Dice.changeDifficultyTo model.dice ( Maybe.withDefault 6 <| String.toInt x ) ) )
+                            ] []
+                        ]
+                    ]
+                ]
+            ]
+        , div [ class "field" ] 
+            [ label [ class "checkbox" ]
+                [ input 
+                    [ type_ "checkbox" 
+                    , onClick ( SetDice ( Dice.switchWillpower model.dice ) )
+                    ] []
+                , text "Use Willpower"
+                ]
+            ]
+        , div [ class "field" ] 
+            [ label [ class "checkbox" ]
+                [ input 
+                    [ type_ "checkbox" 
+                    , onClick ( SetDice ( Dice.switchSpecialty model.dice ) )
+                    ] []
+                , text "Is Specialty"
+                ]
+            ]
+        , div [ class "field is-horizontal" ] 
+            [ div [ class "field-body" ]
+                [ div [ class "field" ]
+                    [ div [ class "control" ] 
+                        [ button 
+                            [ class "button is-primary is-fullwidth"
+                            , onClick Roll
+                            ] 
+                            [ text "Roll Dice"
+                            ]
+                        ]
+                    ]
+                ]
+            ]
+        ]
+
+viewDiceRoll : Dice -> List Int -> Html Msg
+viewDiceRoll dice rollResult =
+    if List.length rollResult > 0
+    then
+        div [] 
+            [ viewSuccesses ( Dice.successes dice rollResult )
+            , div [ class "square-container" ] 
+                ( List.map ( viewResultDie dice.difficulty ) rollResult )
+            ]
+    else
+        div [] []
+
+viewSuccesses : Int -> Html Msg
+viewSuccesses successes =
+    let
+        color : String
+        color =
+            if successes > 0
+            then " is-success"
+            else " is-danger"
+        successText : String
+        successText =
+            if successes == 0
+            then "Failure"
+            else
+                if successes > 0
+                then
+                    if successes > 1
+                    then ( String.fromInt successes ) ++ " Successes"
+                    else "1 Success"
+                else
+                    if successes < -1
+                        then ( String.fromInt <| negate successes ) ++ " Botches"
+                        else "1 Botch"
+    in
+        div 
+            [ class ( "notification" ++ color ) ] 
+            [ text successText ]
+
+viewResultDie : Int -> Int -> Html Msg
+viewResultDie difficulty result =
+    let
+        color : String
+        color =
+            if result >= difficulty
+            then " is-success"
+            else
+                if result == 1
+                then " is-danger"
+                else ""
+    in
+        div 
+            [ class "square" ] 
+            [ div [ class ( "content notification" ++ color ) ]
+                [ div [ class "label is-large" ] [ text ( String.fromInt result ) ]
+                ]
+            ]
+
 -- subscriptions
 
 subscriptions : Model -> Sub Msg
diff --git a/src/Stats.elm b/src/Stats.elm
index 28f431867e91c743b72b808cc3120d9c0e2ddd94..d2e34eb4bed69f179db67fa7f5bf04f91f74db80 100644
--- a/src/Stats.elm
+++ b/src/Stats.elm
@@ -1,7 +1,6 @@
 module Stats exposing (..)
 
 import BoundedInt exposing (BoundedInt)
-import Svg.Attributes exposing (in_)
 
 type alias Stat =
     { name : String