diff --git a/deno2/deno.cc b/deno2/deno.cc
index bfd76f806f..791c0db8eb 100644
--- a/deno2/deno.cc
+++ b/deno2/deno.cc
@@ -213,19 +213,21 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
   v8::Context::Scope context_scope(context);
 
   auto global = context->Global();
-  // TODO(ry) Add a global namespace object "deno" and move print, sub, and
-  // pub inside that object.
+
+  auto deno_val = v8::Object::New(isolate);
+  CHECK(global->Set(context, deno::v8_str("deno"), deno_val).FromJust());
+
   auto print_tmpl = v8::FunctionTemplate::New(isolate, Print);
   auto print_val = print_tmpl->GetFunction(context).ToLocalChecked();
-  CHECK(global->Set(context, deno::v8_str("denoPrint"), print_val).FromJust());
+  CHECK(deno_val->Set(context, deno::v8_str("print"), print_val).FromJust());
 
   auto sub_tmpl = v8::FunctionTemplate::New(isolate, Sub);
   auto sub_val = sub_tmpl->GetFunction(context).ToLocalChecked();
-  CHECK(global->Set(context, deno::v8_str("denoSub"), sub_val).FromJust());
+  CHECK(deno_val->Set(context, deno::v8_str("sub"), sub_val).FromJust());
 
   auto pub_tmpl = v8::FunctionTemplate::New(isolate, Pub);
   auto pub_val = pub_tmpl->GetFunction(context).ToLocalChecked();
-  CHECK(global->Set(context, deno::v8_str("denoPub"), pub_val).FromJust());
+  CHECK(deno_val->Set(context, deno::v8_str("pub"), pub_val).FromJust());
 
   bool r = Execute(context, js_filename, js_source);
   CHECK(r);
diff --git a/deno2/js/deno.d.ts b/deno2/js/deno.d.ts
index 4c797a5f64..4f8a7bc3d8 100644
--- a/deno2/js/deno.d.ts
+++ b/deno2/js/deno.d.ts
@@ -1,6 +1,11 @@
 // Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
 // All rights reserved. MIT License.
 type MessageCallback = (msg: ArrayBuffer) => void;
-declare function denoSub(channel: string, cb: MessageCallback): void;
-declare function denoPub(channel: string, msg: ArrayBuffer): null | ArrayBuffer;
-declare function denoPrint(x: string): void;
+
+interface Deno {
+  sub(channel: string, cb: MessageCallback): void;
+  pub(channel: string, msg: ArrayBuffer): null | ArrayBuffer;
+  print(x: string): void;
+}
+
+declare let deno: Deno;
diff --git a/deno2/js/main.ts b/deno2/js/main.ts
index baa19638fa..d2d61f419c 100644
--- a/deno2/js/main.ts
+++ b/deno2/js/main.ts
@@ -6,13 +6,13 @@ const globalEval = eval;
 const window = globalEval("this");
 
 window["denoMain"] = () => {
-  denoPrint(`ts.version: ${ts.version}`);
-  const res = denoPub("startDeno2", emptyArrayBuffer());
-  //denoPrint(`after`);
+  deno.print(`ts.version: ${ts.version}`);
+  const res = deno.pub("startDeno2", emptyArrayBuffer());
+  //deno.print(`after`);
   const resUi8 = new Uint8Array(res);
-  denoPrint(`before`);
+  deno.print(`before`);
   const msg = pb.Msg.decode(resUi8);
-  denoPrint(`after`);
+  deno.print(`after`);
   const {
     startCwd: cwd,
     startArgv: argv,
@@ -21,11 +21,11 @@ window["denoMain"] = () => {
     startMainMap: mainMap
   } = msg;
 
-  denoPrint(`cwd: ${cwd}`);
-  denoPrint(`debugFlag: ${debugFlag}`);
+  deno.print(`cwd: ${cwd}`);
+  deno.print(`debugFlag: ${debugFlag}`);
 
   for (let i = 0; i < argv.length; i++) {
-    denoPrint(`argv[${i}] ${argv[i]}`);
+    deno.print(`argv[${i}] ${argv[i]}`);
   }
 };
 
diff --git a/deno2/js/mock_runtime.js b/deno2/js/mock_runtime.js
index 07e5a2ca83..f16161cf7b 100644
--- a/deno2/js/mock_runtime.js
+++ b/deno2/js/mock_runtime.js
@@ -11,7 +11,7 @@ function typedArrayToArrayBuffer(ta) {
 }
 
 function CanCallFunction() {
-  denoPrint("Hello world from foo");
+  deno.print("Hello world from foo");
   return "foo";
 }
 
@@ -27,14 +27,14 @@ function TypedArraySnapshots() {
 }
 
 function PubSuccess() {
-  denoSub((channel, msg) => {
+  deno.sub((channel, msg) => {
     assert(channel === "PubSuccess");
-    denoPrint("PubSuccess: ok");
+    deno.print("PubSuccess: ok");
   });
 }
 
 function PubByteLength() {
-  denoSub((channel, msg) => {
+  deno.sub((channel, msg) => {
     assert(channel === "PubByteLength");
     assert(msg instanceof ArrayBuffer);
     assert(msg.byteLength === 3);
@@ -44,16 +44,16 @@ function PubByteLength() {
 function SubReturnEmpty() {
   const ui8 = new Uint8Array("abc".split("").map(c => c.charCodeAt(0)));
   const ab = typedArrayToArrayBuffer(ui8);
-  let r = denoPub("SubReturnEmpty", ab);
+  let r = deno.pub("SubReturnEmpty", ab);
   assert(r == null);
-  r = denoPub("SubReturnEmpty", ab);
+  r = deno.pub("SubReturnEmpty", ab);
   assert(r == null);
 }
 
 function SubReturnBar() {
   const ui8 = new Uint8Array("abc".split("").map(c => c.charCodeAt(0)));
   const ab = typedArrayToArrayBuffer(ui8);
-  const r = denoPub("SubReturnBar", ab);
+  const r = deno.pub("SubReturnBar", ab);
   assert(r instanceof ArrayBuffer);
   assert(r.byteLength === 3);
   const rui8 = new Uint8Array(r);
@@ -62,8 +62,8 @@ function SubReturnBar() {
 }
 
 function DoubleSubFails() {
-  // denoSub is an internal function and should only be called once from the
+  // deno.sub is an internal function and should only be called once from the
   // runtime.
-  denoSub((channel, msg) => assert(false));
-  denoSub((channel, msg) => assert(false));
+  deno.sub((channel, msg) => assert(false));
+  deno.sub((channel, msg) => assert(false));
 }
diff --git a/deno2/js/package.json b/deno2/js/package.json
index 7bc37323e4..eb6cfd7fdb 100644
--- a/deno2/js/package.json
+++ b/deno2/js/package.json
@@ -1,5 +1,7 @@
 {
   "devDependencies": {
+    "@types/base64-js": "^1.2.5",
+    "@types/source-map-support": "^0.4.1",
     "parcel-bundler": "^1.8.1",
     "protobufjs": "^6.8.6",
     "typescript": "^2.9.1"
diff --git a/deno2/js/yarn.lock b/deno2/js/yarn.lock
index f7ab0c5416..b6294e55ec 100644
--- a/deno2/js/yarn.lock
+++ b/deno2/js/yarn.lock
@@ -45,14 +45,28 @@
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
 
+"@types/base64-js@^1.2.5":
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/@types/base64-js/-/base64-js-1.2.5.tgz#582b2476169a6cba460a214d476c744441d873d5"
+
 "@types/long@^3.0.32":
   version "3.0.32"
   resolved "https://registry.yarnpkg.com/@types/long/-/long-3.0.32.tgz#f4e5af31e9e9b196d8e5fca8a5e2e20aa3d60b69"
 
+"@types/node@*":
+  version "10.3.3"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.3.3.tgz#8798d9e39af2fa604f715ee6a6b19796528e46c3"
+
 "@types/node@^8.9.4":
   version "8.10.19"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.19.tgz#66b5b6325c048cbf4512b7a88b0e79c2ee99d3d2"
 
+"@types/source-map-support@^0.4.1":
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.4.1.tgz#ad77158e8c6695a16629ef82b9fb9dfe7c610ac0"
+  dependencies:
+    "@types/node" "*"
+
 abbrev@1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"