diff --git a/gtk/gtkfontchooser.h b/gtk/gtkfontchooser.h
index f21d391f38..f4bdaae2c4 100644
--- a/gtk/gtkfontchooser.h
+++ b/gtk/gtkfontchooser.h
@@ -49,6 +49,7 @@ typedef gboolean (*GtkFontFilterFunc) (const PangoFontFamily *family,
  * @GTK_FONT_CHOOSER_LEVEL_FAMILY: Allow selecting a font family
  * @GTK_FONT_CHOOSER_LEVEL_STYLE: Allow selecting a specific font face
  * @GTK_FONT_CHOOSER_LEVEL_SIZE: Allow selecting a specific font size
+ * @GTK_FONT_CHOOSER_LEVEL_VARIATION: Allow changing OpenType font variation axes
  * @GTK_FONT_CHOOSER_LEVEL_FEATURES: Allow selecting specific OpenType font features
  *
  * This enumeration specifies the granularity of font selection
@@ -58,10 +59,11 @@ typedef gboolean (*GtkFontFilterFunc) (const PangoFontFamily *family,
  * ignore unknown values.
  */
 typedef enum {
-  GTK_FONT_CHOOSER_LEVEL_FAMILY   = 0,
-  GTK_FONT_CHOOSER_LEVEL_STYLE    = 1 << 0,
-  GTK_FONT_CHOOSER_LEVEL_SIZE     = 1 << 1,
-  GTK_FONT_CHOOSER_LEVEL_FEATURES = 1 << 2
+  GTK_FONT_CHOOSER_LEVEL_FAMILY     = 0,
+  GTK_FONT_CHOOSER_LEVEL_STYLE      = 1 << 0,
+  GTK_FONT_CHOOSER_LEVEL_SIZE       = 1 << 1,
+  GTK_FONT_CHOOSER_LEVEL_VARIATIONS = 1 << 2,
+  GTK_FONT_CHOOSER_LEVEL_FEATURES   = 1 << 3
 } GtkFontChooserLevel;
 
 #define GTK_TYPE_FONT_CHOOSER			(gtk_font_chooser_get_type ())
diff --git a/gtk/gtkfontchooserdialog.c b/gtk/gtkfontchooserdialog.c
index d68b78852b..7d9a9471c4 100644
--- a/gtk/gtkfontchooserdialog.c
+++ b/gtk/gtkfontchooserdialog.c
@@ -151,7 +151,7 @@ update_tweak_button (GtkFontChooserDialog *dialog)
     return;
 
   g_object_get (dialog->priv->fontchooser, "level", &level, NULL);
-  if ((level & GTK_FONT_CHOOSER_LEVEL_FEATURES) != 0)
+  if ((level & (GTK_FONT_CHOOSER_LEVEL_VARIATIONS | GTK_FONT_CHOOSER_LEVEL_FEATURES)) != 0)
     gtk_widget_show (dialog->priv->tweak_button);
   else
     gtk_widget_hide (dialog->priv->tweak_button);
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index 57fe21c4e9..3749e8968c 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -116,6 +116,7 @@ struct _GtkFontChooserWidgetPrivate
   GtkWidget *size_slider;
   GtkWidget *size_slider2;
 
+  GtkWidget *axis_grid;
   GtkWidget       *feature_box;
   GtkWidget       *feature_language_combo;
 
@@ -134,6 +135,9 @@ struct _GtkFontChooserWidgetPrivate
 
   GtkFontChooserLevel level;
 
+  GHashTable *axes;
+  gboolean updating_variations;
+
   GList *feature_items;
 
   GAction *tweak_action;
@@ -559,6 +563,7 @@ cursor_changed_cb (GtkTreeView *treeview,
                       FONT_DESC_COLUMN, &desc,
                       -1);
 
+  pango_font_description_set_variations (priv->font_desc, NULL);
   gtk_font_chooser_widget_merge_font_desc (fontchooser,
                                            gtk_delayed_font_description_get (desc),
                                            &iter);
@@ -713,7 +718,7 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, font_name_label);
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, feature_box);
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, feature_language_combo);
-
+  gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, axis_grid);
 
   gtk_widget_class_bind_template_callback (widget_class, text_changed_cb);
   gtk_widget_class_bind_template_callback (widget_class, stop_search_cb);
@@ -754,6 +759,56 @@ change_tweak (GSimpleAction *action,
   g_simple_action_set_state (action, state);
 }
 
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+
+typedef struct {
+  guint32 tag;
+  GtkAdjustment *adjustment;
+  GtkWidget *label;
+  GtkWidget *scale;
+  GtkWidget *spin;
+  GtkWidget *fontchooser;
+} Axis;
+
+static guint
+axis_hash (gconstpointer v)
+{
+  const Axis *a = v;
+
+  return a->tag;
+}
+
+static gboolean
+axis_equal (gconstpointer v1, gconstpointer v2)
+{
+  const Axis *a1 = v1;
+  const Axis *a2 = v2;
+
+  return a1->tag == a2->tag;
+}
+
+static void
+axis_remove (gpointer key,
+             gpointer value,
+             gpointer data)
+{
+  Axis *a = value;
+
+  gtk_widget_destroy (a->label);
+  gtk_widget_destroy (a->scale);
+  gtk_widget_destroy (a->spin);
+}
+
+static void
+axis_free (gpointer v)
+{
+  Axis *a = v;
+
+  g_free (a);
+}
+
+#endif
+
 static void
 gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 {
@@ -764,6 +819,10 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 
   gtk_widget_init_template (GTK_WIDGET (fontchooser));
 
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+  priv->axes = g_hash_table_new_full (axis_hash, axis_equal, NULL, axis_free);
+#endif
+
   /* Default preview string  */
   priv->preview_text = g_strdup (pango_language_get_sample_string (NULL));
   priv->show_preview_entry = TRUE;
@@ -778,9 +837,9 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 
   /* Set the upper values of the spin/scale with G_MAXINT / PANGO_SCALE */
   gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->size_spin),
-			     1.0, (gdouble)(G_MAXINT / PANGO_SCALE));
+                             1.0, (gdouble)(G_MAXINT / PANGO_SCALE));
   gtk_adjustment_set_upper (gtk_range_get_adjustment (GTK_RANGE (priv->size_slider)),
-			    (gdouble)(G_MAXINT / PANGO_SCALE));
+                            (gdouble)(G_MAXINT / PANGO_SCALE));
 
   /* Setup treeview/model auxilary functions */
   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
@@ -1099,6 +1158,9 @@ gtk_font_chooser_widget_finalize (GObject *object)
 
   g_list_free_full (priv->feature_items, g_free);
 
+  if (priv->axes)
+    g_hash_table_unref (priv->axes);
+
   g_free (priv->font_features);
 
   G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->finalize (object);
@@ -1374,6 +1436,214 @@ gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser)
 
 #if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
 
+/* OpenType variations */
+
+#define FixedToFloat(f) (((float)(f))/65536.0)
+
+static void
+add_font_variations (GtkFontChooserWidget *fontchooser,
+                     GString              *s)
+{
+  GHashTableIter iter;
+  Axis *axis;
+  const char *sep = "";
+  char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+  g_hash_table_iter_init (&iter, fontchooser->priv->axes);
+  while (g_hash_table_iter_next (&iter, (gpointer *)NULL, (gpointer *)&axis))
+    {
+      char tag[5];
+      double value;
+
+      tag[0] = (axis->tag >> 24) & 0xff;
+      tag[1] = (axis->tag >> 16) & 0xff;
+      tag[2] = (axis->tag >> 8) & 0xff;
+      tag[3] = (axis->tag >> 0) & 0xff;
+      tag[4] = '\0';
+      value = gtk_adjustment_get_value (axis->adjustment);
+      g_string_append_printf (s, "%s%s=%s", sep, tag, g_ascii_dtostr (buf, sizeof(buf), value));
+      sep = ",";
+    }
+}
+
+static void
+adjustment_changed (GtkAdjustment *adjustment,
+                    Axis          *axis)
+{
+  GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (axis->fontchooser);
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  PangoFontDescription *font_desc;
+  GString *s;
+
+  priv->updating_variations = TRUE;
+
+  s = g_string_new ("");
+  add_font_variations (fontchooser, s);
+
+  if (s->len > 0)
+    {
+      font_desc = pango_font_description_new ();
+      pango_font_description_set_variations (font_desc, s->str);
+      gtk_font_chooser_widget_take_font_desc (fontchooser, font_desc);
+    }
+
+  g_string_free (s, TRUE);
+
+  priv->updating_variations = FALSE;
+}
+
+static gboolean
+should_show_axis (FT_Var_Axis *ax)
+{
+  /* FIXME use FT_Get_Var_Axis_Flags */
+  if (ax->tag == FT_MAKE_TAG ('o', 'p', 's', 'z'))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+is_named_instance (FT_Face face)
+{
+  return (face->face_index >> 16) > 0;
+}
+
+static struct {
+  guint32 tag;
+  const char *name;
+} axis_names[] = {
+  { FT_MAKE_TAG ('w', 'd', 't', 'h'), N_("Width") },
+  { FT_MAKE_TAG ('w', 'g', 'h', 't'), N_("Weight") },
+  { FT_MAKE_TAG ('i', 't', 'a', 'l'), N_("Italic") },
+  { FT_MAKE_TAG ('s', 'l', 'n', 't'), N_("Slant") },
+  { FT_MAKE_TAG ('o', 'p', 's', 'z'), N_("Optical Size") },
+};
+
+static gboolean
+add_axis (GtkFontChooserWidget *fontchooser,
+          FT_Face               face,
+          FT_Var_Axis          *ax,
+          FT_Fixed              value,
+          int                   row)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  Axis *axis;
+  const char *name;
+  int i;
+
+  axis = g_new (Axis, 1);
+  axis->tag = ax->tag;
+  axis->fontchooser = GTK_WIDGET (fontchooser);
+
+  name = ax->name;
+  for (i = 0; i < G_N_ELEMENTS (axis_names); i++)
+    {
+      if (axis_names[i].tag == ax->tag)
+        {
+          name = _(axis_names[i].name);
+          break;
+        }
+    }
+  axis->label = gtk_label_new (name);
+  gtk_widget_show (axis->label);
+  gtk_widget_set_halign (axis->label, GTK_ALIGN_START);
+  gtk_widget_set_valign (axis->label, GTK_ALIGN_BASELINE);
+  gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->label, 0, row, 1, 1);
+  axis->adjustment = gtk_adjustment_new ((double)FixedToFloat(value),
+                                         (double)FixedToFloat(ax->minimum),
+                                         (double)FixedToFloat(ax->maximum),
+                                         1.0, 10.0, 0.0);
+  axis->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, axis->adjustment);
+  gtk_widget_show (axis->scale);
+  gtk_scale_add_mark (GTK_SCALE (axis->scale), (double)FixedToFloat(ax->def), GTK_POS_TOP, NULL);
+  gtk_widget_set_valign (axis->scale, GTK_ALIGN_BASELINE);
+  gtk_widget_set_hexpand (axis->scale, TRUE);
+  gtk_widget_set_size_request (axis->scale, 100, -1);
+  gtk_scale_set_draw_value (GTK_SCALE (axis->scale), FALSE);
+  gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->scale, 1, row, 1, 1);
+  axis->spin = gtk_spin_button_new (axis->adjustment, 0, 0);
+  gtk_widget_show (axis->spin);
+  g_signal_connect (axis->spin, "output", G_CALLBACK (output_cb), fontchooser);
+  gtk_widget_set_valign (axis->spin, GTK_ALIGN_BASELINE);
+  gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->spin, 2, row, 1, 1);
+
+  g_hash_table_add (priv->axes, axis);
+
+  adjustment_changed (axis->adjustment, axis);
+  g_signal_connect (axis->adjustment, "value-changed", G_CALLBACK (adjustment_changed), axis);
+  if (is_named_instance (face) || !should_show_axis (ax))
+    {
+      gtk_widget_hide (axis->label);
+      gtk_widget_hide (axis->scale);
+      gtk_widget_hide (axis->spin);
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchooser)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  PangoFont *pango_font;
+  FT_Face ft_face;
+  FT_MM_Var *ft_mm_var;
+  FT_Error ret;
+  gboolean has_axis = FALSE;
+
+  if (priv->updating_variations)
+    return FALSE;
+
+  g_hash_table_foreach (priv->axes, axis_remove, NULL);
+  g_hash_table_remove_all (priv->axes);
+
+  if ((priv->level & GTK_FONT_CHOOSER_LEVEL_VARIATIONS) == 0)
+    return FALSE;
+
+  pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
+                                        priv->font_desc);
+  ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
+
+  ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
+  if (ret == 0)
+    {
+      int i;
+      FT_Fixed *coords;
+
+      coords = g_new (FT_Fixed, ft_mm_var->num_axis);
+      for (i = 0; i < ft_mm_var->num_axis; i++)
+        coords[i] = ft_mm_var->axis[i].def;
+
+      if (ft_face->face_index > 0)
+        {
+          int instance_id = ft_face->face_index >> 16;
+          if (instance_id && instance_id <= ft_mm_var->num_namedstyles)
+            {
+              FT_Var_Named_Style *instance = &ft_mm_var->namedstyle[instance_id - 1];
+              memcpy (coords, instance->coords, ft_mm_var->num_axis * sizeof (*coords));
+            }
+        }
+
+      for (i = 0; i < ft_mm_var->num_axis; i++)
+        {
+          if (add_axis (fontchooser, ft_face, &ft_mm_var->axis[i], coords[i], i + 4))
+            has_axis = TRUE;
+        }
+
+      g_free (coords);
+      free (ft_mm_var);
+    }
+
+  pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
+  g_object_unref (pango_font);
+
+  return has_axis;
+}
+
+/* OpenType features */
+
 static void
 add_script (GtkListStore *store,
             guint         script_index,
@@ -1932,7 +2202,10 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget       *fontchooser
       gtk_font_chooser_widget_update_marks (fontchooser);
 
 #if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
-      has_tweak = gtk_font_chooser_widget_update_font_features (fontchooser);
+      if (gtk_font_chooser_widget_update_font_features (fontchooser))
+        has_tweak = TRUE;
+      if (gtk_font_chooser_widget_update_font_variations (fontchooser))
+        has_tweak = TRUE;
 #endif
 
       g_simple_action_set_enabled (G_SIMPLE_ACTION (priv->tweak_action), has_tweak);
diff --git a/gtk/ui/gtkfontchooserwidget.ui b/gtk/ui/gtkfontchooserwidget.ui
index d6e8e02255..9c5e3c90a1 100644
--- a/gtk/ui/gtkfontchooserwidget.ui
+++ b/gtk/ui/gtkfontchooserwidget.ui
@@ -244,10 +244,10 @@
 -                
                
              
-`
             
-