diff --git a/examples/gif2webp.c b/examples/gif2webp.c
index eeed97b4..060e466d 100644
--- a/examples/gif2webp.c
+++ b/examples/gif2webp.c
@@ -442,14 +442,21 @@ int main(int argc, const char *argv[]) {
                      gif->SWidth, gif->SHeight);
             }
           }
-          // Allocate current buffer
+          // Set definitive canvas size.
+          err = WebPMuxSetCanvasSize(mux, gif->SWidth, gif->SHeight);
+          if (err != WEBP_MUX_OK) {
+            fprintf(stderr, "Invalid canvas size %d x %d\n",
+                    gif->SWidth, gif->SHeight);
+            goto End;
+          }
+          // Allocate current buffer.
           frame.width = gif->SWidth;
           frame.height = gif->SHeight;
           frame.use_argb = 1;
           if (!WebPPictureAlloc(&frame)) goto End;
           WebPUtilClearPic(&frame, NULL);
 
-          // Initialize cache
+          // Initialize cache.
           cache = WebPFrameCacheNew(frame.width, frame.height,
                                     kmin, kmax, allow_mixed);
           if (cache == NULL) goto End;
diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c
index 2c6c5f9a..26b19249 100644
--- a/src/mux/muxedit.c
+++ b/src/mux/muxedit.c
@@ -20,8 +20,10 @@
 // Life of a mux object.
 
 static void MuxInit(WebPMux* const mux) {
-  if (mux == NULL) return;
+  assert(mux != NULL);
   memset(mux, 0, sizeof(*mux));
+  mux->canvas_width_ = 0;     // just to be explicit
+  mux->canvas_height_ = 0;
 }
 
 WebPMux* WebPNewInternal(int version) {
@@ -29,8 +31,7 @@ WebPMux* WebPNewInternal(int version) {
     return NULL;
   } else {
     WebPMux* const mux = (WebPMux*)WebPSafeMalloc(1ULL, sizeof(WebPMux));
-    // If mux is NULL MuxInit is a noop.
-    MuxInit(mux);
+    if (mux != NULL) MuxInit(mux);
     return mux;
   }
 }
@@ -43,7 +44,7 @@ static void DeleteAllImages(WebPMuxImage** const wpi_list) {
 }
 
 static void MuxRelease(WebPMux* const mux) {
-  if (mux == NULL) return;
+  assert(mux != NULL);
   DeleteAllImages(&mux->images_);
   ChunkListDelete(&mux->vp8x_);
   ChunkListDelete(&mux->iccp_);
@@ -54,9 +55,10 @@ static void MuxRelease(WebPMux* const mux) {
 }
 
 void WebPMuxDelete(WebPMux* mux) {
-  // If mux is NULL MuxRelease is a noop.
-  MuxRelease(mux);
-  WebPSafeFree(mux);
+  if (mux != NULL) {
+    MuxRelease(mux);
+    WebPSafeFree(mux);
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -360,6 +362,32 @@ WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
   return MuxSet(mux, kChunks[IDX_ANIM].tag, 1, &anim, 1);
 }
 
+WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
+                                  int width, int height) {
+  WebPMuxError err;
+  if (mux == NULL) {
+    return WEBP_MUX_INVALID_ARGUMENT;
+  }
+  if (width < 0 || height < 0 ||
+      width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
+    return WEBP_MUX_INVALID_ARGUMENT;
+  }
+  if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
+    return WEBP_MUX_INVALID_ARGUMENT;
+  }
+  if ((width * height) == 0 && (width | height) != 0) {
+    // one of width / height is zero, but not both -> invalid!
+    return WEBP_MUX_INVALID_ARGUMENT;
+  }
+  // If we already assembled a VP8X chunk, invalidate it.
+  err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
+  if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
+
+  mux->canvas_width_ = width;
+  mux->canvas_height_ = height;
+  return WEBP_MUX_OK;
+}
+
 //------------------------------------------------------------------------------
 // Delete API(s).
 
@@ -413,9 +441,10 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
   return WEBP_MUX_OK;
 }
 
-static WebPMuxError GetImageCanvasWidthHeight(
-    const WebPMux* const mux, uint32_t flags,
-    int* const width, int* const height) {
+// Returns the tightest dimension for the canvas considering the image list.
+static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux,
+                                          uint32_t flags,
+                                          int* const width, int* const height) {
   WebPMuxImage* wpi = NULL;
   assert(mux != NULL);
   assert(width != NULL && height != NULL);
@@ -513,12 +542,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
     flags |= ALPHA_FLAG;  // Some images have an alpha channel.
   }
 
-  if (flags == 0) {
-    // For Simple Image, VP8X chunk should not be added.
-    return WEBP_MUX_OK;
-  }
-
-  err = GetImageCanvasWidthHeight(mux, flags, &width, &height);
+  err = GetAdjustedCanvasSize(mux, flags, &width, &height);
   if (err != WEBP_MUX_OK) return err;
 
   if (width <= 0 || height <= 0) {
@@ -528,6 +552,19 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
     return WEBP_MUX_INVALID_ARGUMENT;
   }
 
+  if (mux->canvas_width_ != 0 || mux->canvas_height_ != 0) {
+    if (width > mux->canvas_width_ || height > mux->canvas_height_) {
+      return WEBP_MUX_INVALID_ARGUMENT;
+    }
+    width = mux->canvas_width_;
+    height = mux->canvas_height_;
+  }
+
+  if (flags == 0) {
+    // For Simple Image, VP8X chunk should not be added.
+    return WEBP_MUX_OK;
+  }
+
   if (MuxHasAlpha(images)) {
     // This means some frames explicitly/implicitly contain alpha.
     // Note: This 'flags' update must NOT be done for a lossless image
@@ -603,7 +640,13 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
   uint8_t* dst = NULL;
   WebPMuxError err;
 
-  if (mux == NULL || assembled_data == NULL) {
+  if (assembled_data == NULL) {
+    return WEBP_MUX_INVALID_ARGUMENT;
+  }
+  // Clean up returned data, in case something goes wrong.
+  memset(assembled_data, 0, sizeof(*assembled_data));
+
+  if (mux == NULL) {
     return WEBP_MUX_INVALID_ARGUMENT;
   }
 
@@ -649,4 +692,3 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
 }
 
 //------------------------------------------------------------------------------
-
diff --git a/src/mux/muxi.h b/src/mux/muxi.h
index 277d5fba..6a410232 100644
--- a/src/mux/muxi.h
+++ b/src/mux/muxi.h
@@ -65,7 +65,9 @@ struct WebPMux {
   WebPChunk*      anim_;
   WebPChunk*      vp8x_;
 
-  WebPChunk*  unknown_;
+  WebPChunk*      unknown_;
+  int             canvas_width_;
+  int             canvas_height_;
 };
 
 // CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only.
diff --git a/src/mux/muxread.c b/src/mux/muxread.c
index db51cf12..bba09a5d 100644
--- a/src/mux/muxread.c
+++ b/src/mux/muxread.c
@@ -264,6 +264,10 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
                                          // getting all chunks of an image.
         chunk_list = MuxGetChunkListFromId(mux, id);  // List to add this chunk.
         if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
+        if (id == WEBP_CHUNK_VP8X) {  // grab global specs
+          mux->canvas_width_ = GetLE24(data + 12) + 1;
+          mux->canvas_height_ = GetLE24(data + 15) + 1;
+        }
         break;
     }
     data += data_size;
@@ -320,14 +324,20 @@ static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux,
     f = GetLE32(data.bytes + 0);
     w = GetLE24(data.bytes + 4) + 1;
     h = GetLE24(data.bytes + 7) + 1;
-  } else {  // Single image case.
+  } else {
     const WebPMuxImage* const wpi = mux->images_;
-    WebPMuxError err = ValidateForSingleImage(mux);
-    if (err != WEBP_MUX_OK) return err;
-    assert(wpi != NULL);
-    w = wpi->width_;
-    h = wpi->height_;
-    if (wpi->has_alpha_) f |= ALPHA_FLAG;
+    // Grab user-forced canvas size as default.
+    w = mux->canvas_width_;
+    h = mux->canvas_height_;
+    if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) {
+      // single image and not forced canvas size => use dimension of first frame
+      assert(wpi != NULL);
+      w = wpi->width_;
+      h = wpi->height_;
+    }
+    if (wpi != NULL) {
+      if (wpi->has_alpha_) f |= ALPHA_FLAG;
+    }
   }
   if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
 
@@ -537,4 +547,3 @@ WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
 }
 
 //------------------------------------------------------------------------------
-
diff --git a/src/webp/mux.h b/src/webp/mux.h
index eb57f51c..578d9e02 100644
--- a/src/webp/mux.h
+++ b/src/webp/mux.h
@@ -55,7 +55,7 @@
 extern "C" {
 #endif
 
-#define WEBP_MUX_ABI_VERSION 0x0101        // MAJOR(8b) + MINOR(8b)
+#define WEBP_MUX_ABI_VERSION 0x0102        // MAJOR(8b) + MINOR(8b)
 
 // Note: forward declaring enumerations is not allowed in (strict) C and C++,
 // the types are left here for reference.
@@ -105,6 +105,7 @@ WEBP_EXTERN(WebPMux*) WebPNewInternal(int);
 // Creates an empty mux object.
 // Returns:
 //   A pointer to the newly created empty mux object.
+//   Or NULL in case of memory error.
 static WEBP_INLINE WebPMux* WebPMuxNew(void) {
   return WebPNewInternal(WEBP_MUX_ABI_VERSION);
 }
@@ -309,6 +310,24 @@ WEBP_EXTERN(WebPMuxError) WebPMuxGetAnimationParams(
 //------------------------------------------------------------------------------
 // Misc Utilities.
 
+// Sets the canvas size for the mux object. The width and height can be
+// specified explicitly or left as zero (0, 0).
+// * When width and height are specified explicitly, then this frame bound is
+//   enforced during subsequent calls to WebPMuxAssemble() and an error is
+//   reported if any animated frame does not completely fit within the canvas.
+// * When unspecified (0, 0), the constructed canvas will get the frame bounds
+//   from the bounding-box over all frames after calling WebPMuxAssemble().
+// Parameters:
+//   mux - (in) object to which the canvas size is to be set
+//   width - (in) canvas width
+//   height - (in) canvas height
+// Returns:
+//   WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or
+//                               width or height are invalid or out of bounds
+//   WEBP_MUX_OK - on success.
+WEBP_EXTERN(WebPMuxError) WebPMuxSetCanvasSize(WebPMux* mux,
+                                               int width, int height);
+
 // Gets the canvas size from the mux object.
 // Note: This method assumes that the VP8X chunk, if present, is up-to-date.
 // That is, the mux object hasn't been modified since the last call to
@@ -356,7 +375,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxNumChunks(const WebPMux* mux,
 // Note: The content of 'assembled_data' will be ignored and overwritten.
 // Also, the content of 'assembled_data' is allocated using malloc(), and NOT
 // owned by the 'mux' object. It MUST be deallocated by the caller by calling
-// WebPDataClear().
+// WebPDataClear(). It's always safe to call WebPDataClear() upon return,
+// even in case of error.
 // Parameters:
 //   mux - (in/out) object whose chunks are to be assembled
 //   assembled_data - (out) assembled WebP data