diff --git a/README.md b/README.md
index 0d2d7fa..b076b37 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # RPi WS281x Python
 
-This is the official Python distribution of the ws281x library: http://github.com/richardghirst/rpi_ws281x
+This is the official Python distribution of the ws281x library: https://github.com/jgarff/rpi_ws281x
 
 # Installing
 
@@ -11,3 +11,22 @@ Most users should simply run:
 ```
 sudo pip install rpi_ws281x
 ```
+
+## Building
+
+Clone with submodules, and enter library directory:
+```
+git clone --recurse-submodules https://github.com/rpi-ws281x/rpi-ws281x-python.git
+cd rpi-ws281x-python/library
+```
+To rebuild SWIG files if needed ("black" for code re-formatting only):
+```
+swig -python -threads rpi_ws281x.i
+black rpi_ws281x.py
+```
+
+Build and Install:
+```
+python3 setup.py build
+sudo python3 setup.py install
+```
diff --git a/library/rpi_ws281x.i b/library/rpi_ws281x.i
index f362345..bc73bbb 100644
--- a/library/rpi_ws281x.i
+++ b/library/rpi_ws281x.i
@@ -1,6 +1,8 @@
 // SWIG interface file to define rpi_ws281x library python wrapper.
 // Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net)
 
+%nothread;
+
 // Define module name rpi_ws281x.  This will actually be imported under
 // the name _rpi_ws281x following the SWIG & Python conventions.
 %module rpi_ws281x
@@ -92,3 +94,12 @@ static int convert_iarray(PyObject *input, uint8_t *ptr, int size) {
         return &ws->channel[channelnum];
     }
 %}
+
+%thread;
+%inline %{
+    ws2811_return_t ws2811_render_nogil(ws2811_t *ws2811)
+    {
+        return ws2811_render(ws2811);
+    }
+%}
+%nothread;
diff --git a/library/rpi_ws281x.py b/library/rpi_ws281x.py
index 2761371..4a62b71 100644
--- a/library/rpi_ws281x.py
+++ b/library/rpi_ws281x.py
@@ -221,3 +221,7 @@ def ws2811_led_set(channel, lednum, color):
 
 def ws2811_channel_get(ws, channelnum):
     return _rpi_ws281x.ws2811_channel_get(ws, channelnum)
+
+
+def ws2811_render_nogil(ws2811):
+    return _rpi_ws281x.ws2811_render_nogil(ws2811)
diff --git a/library/rpi_ws281x/rpi_ws281x.py b/library/rpi_ws281x/rpi_ws281x.py
index f9d6a8a..b63a2a7 100644
--- a/library/rpi_ws281x/rpi_ws281x.py
+++ b/library/rpi_ws281x/rpi_ws281x.py
@@ -2,7 +2,7 @@
 # Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net)
 import _rpi_ws281x as ws
 import atexit
-
+from threading import Lock
 
 class RGBW(int):
     def __new__(self, r, g=None, b=None, w=None):
@@ -50,6 +50,9 @@ def __init__(self, num, pin, freq_hz=800000, dma=10, invert=False,
         channel, the PWM channel to use (defaults to 0).
         """
 
+        self._mutex = Lock()
+        self._render_func = ws.ws2811_render
+
         if gamma is None:
             # Support gamma in place of strip_type for back-compat with
             # previous version of forked library
@@ -142,12 +145,17 @@ def begin(self):
             str_resp = ws.ws2811_get_return_t_str(resp)
             raise RuntimeError('ws2811_init failed with code {0} ({1})'.format(resp, str_resp))
 
+    def releaseGIL(self, release = True)
+        """Setup option to release GIL during render function."""
+        self._render_func = ws.ws2811_render_nogil if release else ws.ws2811_render
+
     def show(self):
         """Update the display with the data from the LED buffer."""
-        resp = ws.ws2811_render(self._leds)
-        if resp != 0:
-            str_resp = ws.ws2811_get_return_t_str(resp)
-            raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, str_resp))
+        with self._mutex:
+            resp = self._render_func(self._leds)
+            if resp != 0:
+                str_resp = ws.ws2811_get_return_t_str(resp)
+                raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, str_resp))
 
     def setPixelColor(self, n, color):
         """Set LED at position n to the provided 24-bit color value (in RGB order).
diff --git a/library/rpi_ws281x_wrap.c b/library/rpi_ws281x_wrap.c
index 10959fe..f9bbc61 100644
--- a/library/rpi_ws281x_wrap.c
+++ b/library/rpi_ws281x_wrap.c
@@ -13,6 +13,7 @@
 #define SWIGPYTHON
 #endif
 
+#define SWIG_PYTHON_THREADS
 #define SWIG_PYTHON_DIRECTOR_NO_VTABLE
 
 /* -----------------------------------------------------------------------------
@@ -3132,6 +3133,12 @@ SWIG_FromCharPtr(const char *cptr)
         return &ws->channel[channelnum];
     }
 
+
+    ws2811_return_t ws2811_render_nogil(ws2811_t *ws2811)
+    {
+        return ws2811_render(ws2811);
+    }
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -4370,6 +4377,33 @@ SWIGINTERN PyObject *_wrap_ws2811_channel_get(PyObject *SWIGUNUSEDPARM(self), Py
 }
 
 
+SWIGINTERN PyObject *_wrap_ws2811_render_nogil(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  ws2811_t *arg1 = (ws2811_t *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  ws2811_return_t result;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ws2811_t, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ws2811_render_nogil" "', argument " "1"" of type '" "ws2811_t *""'"); 
+  }
+  arg1 = (ws2811_t *)(argp1);
+  {
+    SWIG_PYTHON_THREAD_BEGIN_ALLOW;
+    result = (ws2811_return_t)ws2811_render_nogil(arg1);
+    SWIG_PYTHON_THREAD_END_ALLOW;
+  }
+  resultobj = SWIG_From_int((int)(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 static PyMethodDef SwigMethods[] = {
 	 { "SWIG_PyInstanceMethod_New", SWIG_PyInstanceMethod_New, METH_O, NULL},
 	 { "ws2811_channel_t_gpionum_set", _wrap_ws2811_channel_t_gpionum_set, METH_VARARGS, NULL},
@@ -4423,6 +4457,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "ws2811_led_get", _wrap_ws2811_led_get, METH_VARARGS, NULL},
 	 { "ws2811_led_set", _wrap_ws2811_led_set, METH_VARARGS, NULL},
 	 { "ws2811_channel_get", _wrap_ws2811_channel_get, METH_VARARGS, NULL},
+	 { "ws2811_render_nogil", _wrap_ws2811_render_nogil, METH_O, NULL},
 	 { NULL, NULL, 0, NULL }
 };
 
@@ -5263,6 +5298,9 @@ SWIG_init(void) {
   SWIG_Python_SetConstant(d, "WS2811_ERROR_SPI_SETUP",SWIG_From_int((int)(WS2811_ERROR_SPI_SETUP)));
   SWIG_Python_SetConstant(d, "WS2811_ERROR_SPI_TRANSFER",SWIG_From_int((int)(WS2811_ERROR_SPI_TRANSFER)));
   SWIG_Python_SetConstant(d, "WS2811_RETURN_STATE_COUNT",SWIG_From_int((int)(WS2811_RETURN_STATE_COUNT)));
+  
+  /* Initialize threading */
+  SWIG_PYTHON_INITIALIZE_THREADS;
 #if PY_VERSION_HEX >= 0x03000000
   return m;
 #else