diff --git a/js/ui/magPrefsUI.js b/js/ui/magPrefsUI.js
new file mode 100644
index 0000000..0c51011
--- /dev/null
+++ b/js/ui/magPrefsUI.js
@@ -0,0 +1,66 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Gtk = imports.gi.Gtk;
+const Clutter = imports.gi.Clutter;
+const Shell = imports.gi.Shell;
+const Big = imports.gi.Big;
+
+function MagPrefsUI (options) {
+
+    var _magnifier = ( options ? options.magnifier : null );
+
+    // create a new window
+    this.win = new Gtk.Window ({ type: Gtk.WindowType.TOPLEVEL });
+    this.win.set_border_width (10);
+    this.win.set_title ("Magnifier Preferences");
+
+    // A vertical box to contain the controls
+    let vBox = new Gtk.VButtonBox();
+
+    // Creates a toggle button to flip whether magnifier is active or not.
+    let togButton = new Gtk.ToggleButton({ label: "Show _Magnifier" });
+    togButton.set_use_underline (true);
+    togButton.set_active (_magnifier.isActive());
+    togButton.connect("clicked", function() {
+        _magnifier.setActive (!(_magnifier.isActive()));
+    });
+    vBox.add (togButton);
+
+    // Creates a toggle button to flip full screen mode.
+    let fullScreenToggle = new Gtk.ToggleButton({ label: "Toggle _Full Screen Mode" });
+    fullScreenToggle.set_use_underline (true);
+    fullScreenToggle.set_active (_magnifier.isFullScreenMode());
+    fullScreenToggle.connect("clicked", function() {
+        if (_magnifier.isFullScreenMode()) {
+            _magnifier.resetFullScreenMode();
+        }
+        else {
+            _magnifier.setFullScreenMode();
+        }
+    });
+    vBox.add (fullScreenToggle);
+
+    // A slider for adjusting the mag factor.
+    let slider = new Gtk.HScale();
+    slider.set_range (1, 10);
+    let [magX, magY] = _magnifier.getMagFactor();
+    slider.set_value (magX);
+    slider.connect ("value-changed", function() {
+        let val = slider.get_value();
+        _magnifier.setMagFactor (val, val);
+    });
+    vBox.add (slider);
+
+    // Put the controls in the window, and show it.
+    this.win.add (vBox);
+    togButton.show();
+    fullScreenToggle.show();
+    slider.show();
+    vBox.show();
+    this.win.show();
+    let [cx, cy] = _magnifier.getSize();
+    let [mx, my] = _magnifier.getMagFactor();
+    this.win.move ((cx/mx)/2, (cy/my)/2);
+
+}   // end MagPrefsUI().
+
diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js
new file mode 100644
index 0000000..0ba2ad6
--- /dev/null
+++ b/js/ui/magnifier.js
@@ -0,0 +1,285 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Shell = imports.gi.Shell;
+const Big = imports.gi.Big;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+const MagPrefsUI = imports.ui.magPrefsUI;
+
+const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
+DEFAULT_BACKGROUND_COLOR.from_pixel (0x2266bbff);
+
+function Magnifier() {
+
+    // If magnifier is showing
+    var _active = false;
+
+    // The Big.Box that is the container root of the magnified view
+    var _magView = null;
+
+    // Clone of the stage's UIGroup.  The single child of _magView.
+    var _uiGroupClone = null;
+
+    // Magnification power in the horizontal direction.  Default is 2x.
+    var _xMagFactor = 2;
+
+    // Magnification power in the vertical direction.  Default is 2x.
+    var _yMagFactor = 2;
+
+    // resetting from full screen mode.  See resetFromFullScreenMode().
+    var _posSize = { left: 0, top: 0, width: 0, height: 0 };
+
+    let global = Shell.Global.get();
+
+    // Outer box is used to draw a frame around the magnified view.
+    const BORDER_COLOR = new Clutter.Color();
+    BORDER_COLOR.from_pixel(0x800000ff);
+    _magView = new Big.Box ({ border: 8, border_color: BORDER_COLOR, name: "Magnifier" });
+
+    // Hard coded (default) position of magnified view, for now.
+    // Note:  should get this from preferences.
+    let posX = global.stage.get_width()/2;
+    let posY = global.stage.get_height() * .66;
+    _magView.set_position (0, posY);
+
+    // Use group to clip the contents
+    let aGroup = new Clutter.Group({ clip_to_allocation: true });
+    _magView.append(aGroup, Big.BoxPackFlags.EXPAND);
+    aGroup.set_size(posX, posY/2);
+
+    // Add a background for when the magnified uiGroup is scrolled
+    // out of view (don't want to see desktop showing through).
+    let background = new Clutter.Rectangle ({ color: DEFAULT_BACKGROUND_COLOR });
+    aGroup.add_actor (background);
+
+    // Clone the group that contains all of UI on the screen.  This is the
+    // chrome, the windows, etc.
+    _uiGroupClone = new Clutter.Clone ({ source: global.uiGroup });
+    aGroup.add_actor (_uiGroupClone);
+
+    // The contents of the clone are scaled from their original size
+    // to the allocated size of the clone, this is not useful to us
+    // since we want to scale by a fixed factor; the size of the window
+    // group is unpredictable because windows can go outside the
+    // screen, but we can set it to something fixed, since the size
+    // of a clutter actor doesn't affect clipping or display. Then we
+    // set the size of the clone to the same size. Actual size picked
+    // doesn't matter - use the size of the screen.
+    global.uiGroup.set_size (global.screen_width, global.screen_height);
+    background.set_size (global.screen_width, global.screen_height);
+    _uiGroupClone.set_size (global.screen_width, global.screen_height);
+
+    // Fixed factor scaling
+    _uiGroupClone.set_scale (_xMagFactor, _yMagFactor);
+
+    // Record current position and size of the magnified view.
+    let [ex, wy] = _magView.get_size();
+    _posSize.left = ex;
+    _posSize.top = wy;
+    [ex, wy] = _magView.get_size();
+    _posSize.width = ex;
+    _posSize.height = wy;
+
+    _magView.hide();
+    global.stage.add_actor (_magView);
+
+    /**
+     * setActive:
+     * @activate  Boolean to activate of de-activate the Magnifier.
+     *            The magnified view is shown or hidden depending
+     *            on the sense of the argument, but remains an
+     *            actor on the stage.
+     */
+    Magnifier.prototype.setActive = function (activate) {
+        if (activate) {
+          _magView.show();
+          _active = true;
+        }
+        else {
+          _magView.hide();
+          _active = false;
+        }
+    }   // end setActive().
+
+    /**
+     * isActive:
+     * @return  Whether the magnifier is active (boolean).
+     */
+    Magnifier.prototype.isActive = function() {
+        return _active;
+
+    }   // end isActive().
+
+   /**
+     * setMagFactor:
+     * @inXmagFactor    the power to set the horizontal magnification factor to
+     *                  of the magnified view, e.g. 2x, 10x, etc.  If less than
+     *                  or equal to zero, it is treated as 1x.
+     * @inYmagFactor    the power to set the vertical magnification factor to
+     *                  of the magnified view.  If less thanor equal to zero,
+     *                  it is treated as 1x.
+     */
+    Magnifier.prototype.setMagFactor = function (inXMagFactor, inYMagFactor) {
+        _xMagFactor = ( inXMagFactor <= 0 ? 1 : inXMagFactor );
+        _yMagFactor = ( inYMagFactor <= 0 ? 1 : inYMagFactor );
+        _uiGroupClone.set_scale (_xMagFactor, _yMagFactor);
+
+    }   // end setMagFactor().
+
+    /**
+     * getMagFactor:
+     * @return  an array, {x, y}, that contains the horizontal and vertical
+     *          magnficaction factors (2x, 10X, etc.).
+     */
+    Magnifier.prototype.getMagFactor = function() {
+        return [_xMagFactor, _yMagFactor];
+
+    }   // end getMagFactor().
+
+    /**
+     * Consult if the Magnifier can magnify in full-screen mode.
+     * @return  true if it can or false otherwise.
+     */
+    Magnifier.prototype.fullScreenCapable = function() {
+        return true;
+
+    }   // end fullScreenCapable().
+
+    /**
+     * setSize:
+     * @inWidth     The width to set the magnified view to.
+     * @inHeight    The height to set the magnified view to.
+     */
+    Magnifier.prototype.setSize = function (inWidth, inHeight) {
+        _magView.set_size (inWidth, inHeight);
+
+    }   // end setSize().
+
+    /**
+     * getSize:
+     * @return  an array, [width, height], that specifies the size of the
+     *          magnified view.
+     */
+    Magnifier.prototype.getSize = function() {
+        return _magView.get_size();
+
+    }   // end getSize().
+
+    /**
+     * setPosition:
+     * @inX     The x-coord of the new position.
+     * @inY     The y-coord of the new position.
+     * @return  The old position [x, y].
+     */
+    Magnifier.prototype.setPosition = function (inX, inY) {
+        let [x, y] = _magView.get_position();
+        _magView.set_position (inX, inY);
+        return [x, y];
+
+    }   // end setPosition().
+
+    /**
+     * getPosition:
+     * @return  an array, [x, y], that gives the position of the
+     *          magnified view.
+     */
+    Magnifier.prototype.getPosition = function() {
+        return _magView.get_position();
+
+    }   // end getPosition().
+
+    /**
+     * getCentre:
+     * @return  an array, [x, y], that gives the centre of the
+     *          magnified view.
+     */
+    Magnifier.prototype.getCentre = function() {
+        let [x, y] = _magView.get_size();
+        return [x/2, y/2];
+
+    }   // end getPosition().
+
+    /**
+     * setFullScreenMode:
+     * Set the Magnifier to full-screen mode.
+     */
+    Magnifier.prototype.setFullScreenMode = function() {
+        if (!this.isFullScreenMode()) {
+            let [x, y] = _magView.get_position();
+            _posSize.left = x;
+            _posSize.top = y;
+
+            [x, y] = _magView.get_size();
+            _posSize.width = x;
+            _posSize.height = y;
+
+            _magView.set_position (0, 0);
+            _magView.set_size (global.screen_width, global.screen_height);
+        }
+
+    }   // end setFullScreenMode().
+
+    /**
+     * resetFullScreenMode:
+     * Reset the size of the magnifier view back to the size before
+     * it was set to full screen mode.
+     */
+    Magnifier.prototype.resetFullScreenMode = function() {
+        _magView.set_position (_posSize.left, _posSize.top);
+        _magView.set_size (_posSize.width, _posSize.height);
+
+    }   // end resetFullScreenMode().
+
+    /**
+     * isFullScreenMode:
+     * Does the magnified view occupy the whole screen?
+     */
+    Magnifier.prototype.isFullScreenMode = function() {
+        let retVal = false;
+        let [x, y] = _magView.get_position();
+        if (x == 0 && y == 0) {
+            [x, y] = _magView.get_size();
+            if (x == global.screen_width && y == global.screen_height) {
+                retVal = true;
+            }
+        }
+        return retVal;
+
+    }   // end isFullScreenMode().
+
+    /**
+     * scrollToMousePos
+     * Centre the magnified view to the position of the mouse.
+     * @return  an array, [x, y], of the new position of the clone of
+     * the UI group
+     */
+    Magnifier.prototype.scrollToMousePos = function() {
+        let [objUnder, mx, my, mask] =
+            Gdk.Screen.get_default().get_root_window().get_pointer();
+
+        // coords in _uiGroupClone are scaled by the magnification factor, so
+        // mouse position in that clone is likewise scaled.
+        let [X, Y] = this.getMagFactor();
+        mx *= X;
+        my *= Y;
+
+        // get the centre of the magnified view, move UI clone
+        // to centre based on mouse location, and return new
+        // position.
+        let [cx, cy] = this.getCentre();
+        _uiGroupClone.set_position (cx - mx, cy - my);
+        return _uiGroupClone.get_position();
+
+    }   // end scrollToMousePos().
+
+    // Create the prefs UI.
+    Magnifier.prototype.prefsUI = new MagPrefsUI.MagPrefsUI ({magnifier: this});
+
+    // Track the mouse.
+    // Note:  should get time out interval from settings.
+    Mainloop.timeout_add (50, Lang.bind (this, this.scrollToMousePos));
+
+}   // end Magnifier().
diff --git a/js/ui/main.js b/js/ui/main.js
index c6b09d9..3f3cd97 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -20,6 +20,7 @@ const ShellDBus = imports.ui.shellDBus;
 const Sidebar = imports.ui.sidebar;
 const Tweener = imports.ui.tweener;
 const WindowManager = imports.ui.windowManager;
+const Magnifier = imports.ui.magnifier;
 
 const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
 DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
@@ -35,6 +36,8 @@ let recorder = null;
 let shellDBusService = null;
 let modalCount = 0;
 let modalActorFocusStack = [];
+let uiGroup = null;
+let magnifier = null;
 
 function start() {
     // Add a binding for "global" in the global JS namespace; (gjs
@@ -86,6 +89,16 @@ function start() {
     sidebar = new Sidebar.Sidebar();
     wm = new WindowManager.WindowManager();
 
+    // Set up stage hierarchy for the Magnifier -- it
+    // clones everything but the Magnifier actor itself.
+    //
+    uiGroup = new Clutter.Group ({ name: "uiGroup"});
+    global.window_group.reparent (uiGroup);
+    global.overlay_group.reparent (uiGroup);
+    chrome.actor.reparent (uiGroup);
+    global.stage.add_actor (uiGroup);
+    global.uiGroup = uiGroup;
+
     global.screen.connect('toggle-recording', function() {
         if (recorder == null) {
             recorder = new Shell.Recorder({ stage: global.stage });
@@ -108,6 +121,13 @@ function start() {
 
     global.stage.connect('captured-event', _globalKeyPressHandler);
 
+    // Install magnifier.
+    // Note:  to make active, call magnifier.setActive (true);
+    //
+    magnifier = new Magnifier.Magnifier();
+    global.magnifier = magnifier;
+
+
     Mainloop.idle_add(_removeUnusedWorkspaces);
 }
 
