diff --git a/index.html b/index.html
index b410a21..1cd65b1 100644
--- a/index.html
+++ b/index.html
@@ -18,7 +18,7 @@
           rel='stylesheet' type='text/css'>
 </head>
 <body>
-<div class="container">
+<div class="container" id="app">
     <div class="header text-center">
         <h1>VANITY-ETH</h1>
         <p>Vanity ETH address generator</p>
@@ -46,22 +46,22 @@
     <div class="row">
         <div class="col-md-6">
             <div class="panel form">
-                <form id="form">
+                <form :class="{error: inputError}">
                     <div class="error-text">Numbers and letters from A to F only</div>
-                    <input type="text" placeholder="Prefix" id="prefix">
+                    <input type="text" placeholder="Prefix" v-model="input.prefix" :disabled="running">
                     <div class="check">
                         <label class="checkbox">
-                            <input type="checkbox" name="checkbox" id="checksum" checked="">
+                            <input type="checkbox" name="checkbox" checked="" v-model="input.checksum" :disabled="running">
                             <i class="left"> </i>
                             Case-sensitive
                         </label>
                     </div>
                     <div class="row">
                         <div class="col-lg-6 col-sm-12">
-                            <input type="button" id="gen" value="Generate">
+                            <input type="button" value="Generate" @click="startGen" :disabled="running || inputError">
                         </div>
                         <div class="col-lg-6 col-sm-12">
-                            <input type="button" id="stop" value="Stop" disabled="">
+                            <input type="button" value="Stop" @click="stopGen" :disabled="!running">
                         </div>
                     </div>
                 </form>
@@ -69,16 +69,16 @@
         </div>
         <div class="col-md-6">
             <div class="panel statistics">
-                <div>Difficulty: <span id="difficulty" class="output">1</span></div>
-                <div>Generated: <span id="counter" class="output">0 addresses</span></div>
-                <div>Speed: <span id="speed" class="output">0 addr/s</span></div>
-                <div>Status: <span id="status" class="output">Waiting</span></div>
+                <div>Difficulty: <span class="output" v-text="difficulty">1</span></div>
+                <div>Generated: <span class="output" v-text="count + (count === 1 ? ' address' : ' addresses')">0 addresses</span></div>
+                <div>Speed: <span class="output" v-text="speed">0 addr/s</span></div>
+                <div>Status: <span class="output" v-text="status">Waiting</span></div>
                 <!--Probability:-->
                 <div class="probability">
-                    <div class="probability-bar" id="probability-bar" style="width:0"></div>
+                    <div class="probability-bar" :style="'width:' + probability + '%'"></div>
                 </div>
                 <div class="percentage">
-                    <h5 id="probability">0%</h5>
+                    <h5 v-text="probability + '%'">0%</h5>
                     <div>Probability</div>
                 </div>
             </div>
@@ -87,8 +87,8 @@
     <div class="row">
         <div class="col-md-12">
             <div class="panel result">
-                <div>Address: <span id="address" class="output"></span></div>
-                <div>Private key: <span id="private-key" class="output"></span></div>
+                <div>Address: <span class="output" v-text="result.address"></span></div>
+                <div>Private key: <span class="output" v-text="result.privateKey"></span></div>
             </div>
         </div>
     </div>
@@ -96,6 +96,7 @@
 
 <!--JS-->
 <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>-->
+<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
 <script src="js/bundle.js" type="text/javascript"></script>
 <script src="js/index.js" type="text/javascript"></script>
 </body>
diff --git a/js/index.js b/js/index.js
index 044e288..97cc71e 100644
--- a/js/index.js
+++ b/js/index.js
@@ -1,120 +1,88 @@
 /* eslint-env browser */
-/* global vanity:false */
+/* global vanity:false, Vue:false */
 
-let count = 0;
-let stop = false;
-let lastTick = null;
-let firstTick = null;
-let difficulty = 0;
-const step = 250;
-const elements = {};
-const ids = {
-	counter: 'counter',
-	speed: 'speed',
-	probability: 'probability',
-	probabilityBar: 'probability-bar',
-	status: 'status',
-	genBtn: 'gen',
-	stopBtn: 'stop',
-	form: 'form'
-};
+// eslint-disable-next-line no-new
+new Vue({
+	el: '#app',
+	data: {
+		count: 0,
+		lastTick: null,
+		firstTick: null,
+		running: false,
+		step: 250,
+		speed: '0 addr/s',
+		status: 'Waiting',
+		result: {
+			address: '',
+			privateKey: ''
+		},
+		input: {
+			prefix: '',
+			checksum: true
+		}
+	},
 
-const parseInput = () => {
-	const input = {
-		prefix: document.getElementById('prefix').value,
-		checksum: document.getElementById('checksum').checked
-	};
+	computed: {
+		inputError() {
+			return !vanity.isValidHex(this.input.prefix);
+		},
+		difficulty() {
+			return this.inputError ? 'N/A' : vanity.computeDifficulty(this.input.prefix, this.input.checksum);
+		},
+		probability() {
+			return Math.round(10000 * vanity.computeProbability(this.difficulty, this.count)) / 100;
+		}
+	},
+	methods: {
+		incrementCounter(incr) {
+			this.count += incr;
+			const currentTick = performance.now();
+			this.speed = incr > 0 ? Math.floor(1000 * incr / (currentTick - this.lastTick)) + ' addr/s' : '0 addr/s';
+			this.lastTick = currentTick;
+		},
 
-	if (!vanity.isValidHex(input.prefix)) {
-		elements.form.className = 'error';
-		return;
+		displayResult(result) {
+			this.incrementCounter(result.attempts);
+			this.result.address = result.address;
+			this.result.privateKey = result.privKey;
+			this.status = 'Address found';
+			this.speed = Math.floor(1000 * this.count / (performance.now() - this.firstTick)) + ' addr/s';
+		},
+
+		clearResult() {
+			this.result.address = '';
+			this.result.privateKey = '';
+		},
+
+		generate() {
+			const add = vanity.getVanityWallet(this.input.prefix, this.input.checksum, this.step);
+			if (add !== null) {
+				this.running = false;
+				return this.displayResult(add);
+			}
+
+			this.incrementCounter(this.step);
+
+			if (!this.running) {
+				this.status = 'Stopped';
+				return;
+			}
+
+            // Use setTimeout to let the browser render
+			setTimeout(() => this.generate(), 0);
+		},
+
+		startGen() {
+			this.firstTick = performance.now();
+			this.incrementCounter(-this.count);
+			this.clearResult();
+			this.running = true;
+
+			setTimeout(() => this.generate(), 0);
+		},
+
+		stopGen() {
+			this.running = false;
+		}
 	}
-
-	elements.form.className = '';
-	difficulty = vanity.computeDifficulty(input.prefix, input.checksum);
-	document.getElementById('difficulty').innerText = difficulty.toString();
-	return input;
-};
-
-const incrementCounter = incr => {
-	count += incr;
-	elements.counter.innerText = count.toString() + (count === 1 ? ' address' : ' addresses');
-
-	const currentTick = performance.now();
-	elements.speed.innerText = incr > 0 ? Math.floor(1000 * incr / (currentTick - lastTick)) + ' addr/s' : '0 addr/s';
-	lastTick = currentTick;
-};
-
-const updateStats = () => {
-	const prob = Math.round(10000 * vanity.computeProbability(difficulty, count)) / 100;
-	elements.probability.innerText = prob + '%';
-	elements.probabilityBar.style.width = prob + '%';
-};
-
-const displayResult = result => {
-	incrementCounter(result.attempts);
-	document.getElementById('address').innerText = result.address;
-	document.getElementById('private-key').innerText = result.privKey;
-	elements.status.innerText = 'Address found';
-	console.info('Average speed: ' + Math.floor(1000 * count / (performance.now() - firstTick)) + ' addr/s');
-	updateStats();
-};
-
-const clearResult = () => {
-	document.getElementById('address').innerText = '';
-	document.getElementById('private-key').innerText = '';
-	elements.status.innerText = 'Running';
-	updateStats();
-};
-
-const toggleButtons = () => {
-	const enabled = stop ? elements.genBtn : elements.stopBtn;
-	const disabled = stop ? elements.stopBtn : elements.genBtn;
-	enabled.removeAttribute('disabled');
-	disabled.setAttribute('disabled', '');
-};
-
-const generate = input => {
-	const add = vanity.getVanityWallet(input.prefix, input.checksum, step);
-	if (add !== null) {
-		stop = true;
-		toggleButtons();
-		return displayResult(add);
-	}
-
-	incrementCounter(step);
-	updateStats();
-
-	if (stop) {
-		elements.status.innerText = 'Stopped';
-		return;
-	}
-
-    // Use setTimeout to let the browser render
-	setTimeout(() => generate(input), 0);
-};
-
-for (const e in ids) { // eslint-disable-line guard-for-in
-	elements[e] = document.getElementById(ids[e]);
-}
-
-// Add event listeners on buttons
-elements.genBtn.addEventListener('click', () => {
-	firstTick = performance.now();
-	incrementCounter(-count);
-	clearResult();
-
-	const input = parseInput();
-	stop = false;
-	toggleButtons();
-
-	setTimeout(() => generate(input), 0);
 });
-
-elements.stopBtn.addEventListener('click', () => {
-	stop = true;
-	toggleButtons();
-});
-
-elements.form.addEventListener('change', () => parseInput());
-elements.form.addEventListener('keyup', () => parseInput());