diff --git a/Common/Context.cpp b/Common/Context.cpp
index a81c16636dbc54a7a2bbeb782b7e90afe7d07455..05188206f8d4860f868e1b31730a124250fbe9ba 100644
--- a/Common/Context.cpp
+++ b/Common/Context.cpp
@@ -98,6 +98,7 @@ CTX::CTX() : debugSurface(-1), gamepad(0)
   mesh.minCircPoints = mesh.order = 0;
   mesh.secondOrderLinear = mesh.secondOrderIncomplete = 0;
   mesh.lightLines = 2;
+  mesh.lcCallback = NULL;
 }
 
 CTX::~CTX()
diff --git a/Common/Context.h b/Common/Context.h
index a7248173c5c013361c156fdfe0309980d6b2ab8d..7e65023c8fc57c4094c2e1db1cd2a22c382f3ddd 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -42,6 +42,7 @@ struct contextMeshOptions {
   int renumber, compoundClassify, reparamMaxTriangles;
   double compoundLcFactor;
   unsigned int randomSeed;
+  double (*lcCallback)(int dim, int tag, double x, double y, double z);
   // mesh IO
   int fileFormat, firstElementTag, firstNodeTag;
   double mshFileVersion, medFileMinorVersion, scalingFactor;
diff --git a/Common/gmsh.cpp b/Common/gmsh.cpp
index 31f7fa0aa2e1d00ac4554fff53078b81a296afbb..293bfc8d49de8870970fa4bbc5c5896ae6df3d5f 100644
--- a/Common/gmsh.cpp
+++ b/Common/gmsh.cpp
@@ -4199,6 +4199,19 @@ GMSH_API void gmsh::model::mesh::setSizeAtParametricPoints(
   }
 }
 
+GMSH_API void gmsh::model::mesh::setSizeCallback(
+  double (*callback)(int dim, int tag, double x, double y, double z))
+{
+  _checkInit();
+  CTX::instance()->mesh.lcCallback = callback;
+}
+
+GMSH_API void gmsh::model::mesh::removeSizeCallback()
+{
+  _checkInit();
+  CTX::instance()->mesh.lcCallback = NULL;
+}
+
 GMSH_API void
 gmsh::model::mesh::setTransfiniteCurve(const int tag, const int numNodes,
                                        const std::string &meshType,
diff --git a/Mesh/BackgroundMeshTools.cpp b/Mesh/BackgroundMeshTools.cpp
index 0c54b8cb6ca6a92ae278459dc3f9d58912ccb131..80bdc77098a2d9166cde57222ff24a0a0bea016d 100644
--- a/Mesh/BackgroundMeshTools.cpp
+++ b/Mesh/BackgroundMeshTools.cpp
@@ -229,10 +229,20 @@ double BGM_MeshSizeWithoutScaling(GEntity *ge, double U, double V, double X,
   // global lc from entity
   double l4 = ge ? ge->getMeshSize() : MAX_LC;
 
-  double l5 = (ge && ge->dim() == 1) ? ((GEdge*)ge)->prescribedMeshSizeAtParam(U) : MAX_LC;
+  double l5 = (ge && ge->dim() == 1) ?
+    ((GEdge*)ge)->prescribedMeshSizeAtParam(U) : MAX_LC;
+
+  // lc from callback
+  double l6 = MAX_LC;
+  if(CTX::instance()->mesh.lcCallback) {
+    int dim = (ge ? ge->dim() : -1);
+    int tag = (ge ? ge->tag() : -1);
+    l6 = (*CTX::instance()->mesh.lcCallback)(dim, tag, X, Y, Z);
+  }
 
   // take the minimum
-  double lc = std::min(std::min(std::min(std::min(l1, l2), l3), l4),l5);
+  double lc = std::min(std::min(std::min(std::min(std::min(l1, l2), l3), l4),
+                                l5), l6);
 
   return lc;
 }
diff --git a/Mesh/meshGRegionHxt.cpp b/Mesh/meshGRegionHxt.cpp
index 54f7d049692fade99bd54d982ed4774872784511..9dd93743825cf7a364ecc111e5cfe24d8452c596 100644
--- a/Mesh/meshGRegionHxt.cpp
+++ b/Mesh/meshGRegionHxt.cpp
@@ -443,7 +443,8 @@ static HXTStatus _meshGRegionHxt(std::vector<GRegion *> &regions)
     },
     { // nodalSize
 
-      // FIXME: put NULL when the callback is not needed (when we use the interpolated point size anyway)
+      // FIXME: put NULL when the callback is not needed (when we use the
+      // interpolated point size anyway)
       nodalSizesCallBack, // HXTStatus (*callback)(double*, size_t, void* userData)
       &regions, // void* meshSizeData;
       CTX::instance()->mesh.lcMin,
diff --git a/api/GenApi.py b/api/GenApi.py
index 1c523deed345e28ce419250c0fb522ef5354f6e7..8553639582dcb6257e11a5d7ebe8cc81e26e51bc 100644
--- a/api/GenApi.py
+++ b/api/GenApi.py
@@ -713,7 +713,9 @@ def ovectorvectorpair(name, value=None, python_value=None, julia_value=None):
     return a
 
 
-def argcargv():
+# special types
+
+def iargcargv():
     a = arg("", None, None, None, "", "", False)
     a.cpp = "int argc = 0, char ** argv = 0"
     a.c_arg = "argc, argv"
@@ -731,6 +733,16 @@ def argcargv():
     a.texi = "(argc = 0)}, @code{argv = []"
     return a
 
+def isizefun(name):
+    a = arg(name, None, None, None, "", "", False)
+    a.cpp = "double (*" + name + ")(int dim, int tag, double x, double y, double z)"
+    a.c_arg = name
+    a.c = a.cpp
+    a.c_pre = ""
+    a.c_post = ""
+    a.cwrap_arg = a.c_arg
+    return a
+
 
 class Module:
     def __init__(self, name, doc):
diff --git a/api/gen.py b/api/gen.py
index fcd5db6f998d6cb39c1c95842860c1bb4e943607..f42c86260653e46ed04005d1a395518f6c926f8f 100644
--- a/api/gen.py
+++ b/api/gen.py
@@ -42,7 +42,7 @@ api = API(version_major, version_minor)
 gmsh = api.add_module('gmsh', 'top-level functions')
 
 doc = '''Initialize Gmsh. This must be called before any call to the other functions in the API. If `argc' and `argv' (or just `argv' in Python or Julia) are provided, they will be handled in the same way as the command line arguments in the Gmsh app. If `readConfigFiles' is set, read system Gmsh configuration files (gmshrc and gmsh-options).'''
-gmsh.add('initialize', doc, None, argcargv(), ibool('readConfigFiles', 'true', 'True', 'true'))
+gmsh.add('initialize', doc, None, iargcargv(), ibool('readConfigFiles', 'true', 'True', 'true'))
 
 doc = '''Finalize Gmsh. This must be called when you are done using the Gmsh API.'''
 gmsh.add('finalize', doc, None)
@@ -377,6 +377,12 @@ mesh.add('setSize', doc, None, ivectorpair('dimTags'), idouble('size'))
 doc = '''Set mesh size constraints at the given parametric points `parametricCoord' on the model entity of dimension `dim' and tag `tag'. Currently only entities of dimension 1 (lines) are handled.'''
 mesh.add('setSizeAtParametricPoints', doc, None, iint('dim'), iint('tag'), ivectordouble('parametricCoord'), ivectordouble('sizes'))
 
+doc = '''Set global mesh size callback. For C and C++ only.'''
+mesh.add_special('setSizeCallback', doc, ['onlycc++'], None, isizefun('callback'))
+
+doc = '''Remove global mesh size callback. For C and C++ only.'''
+mesh.add('removeSizeCallback', doc, None)
+
 doc = '''Set a transfinite meshing constraint on the curve `tag', with `numNodes' nodes distributed according to `meshType' and `coef'. Currently supported types are "Progression" (geometrical progression with power `coef') and "Bump" (refinement toward both extremities of the curve).'''
 mesh.add('setTransfiniteCurve', doc, None, iint('tag'), iint('numNodes'), istring('meshType', '"Progression"'), idouble('coef', '1.'))
 
diff --git a/api/gmsh.h b/api/gmsh.h
index 5678b15f3f0000677a3b9a0826a8a1a20f4943b9..b88666ea7ad219e28fd7541a3e5f94b339f8c8bc 100644
--- a/api/gmsh.h
+++ b/api/gmsh.h
@@ -1169,6 +1169,16 @@ namespace gmsh { // Top-level functions
                                               const std::vector<double> & parametricCoord,
                                               const std::vector<double> & sizes);
 
+      // gmsh::model::mesh::setSizeCallback
+      //
+      // Set global mesh size callback. For C and C++ only.
+      GMSH_API void setSizeCallback(double (*callback)(int dim, int tag, double x, double y, double z));
+
+      // gmsh::model::mesh::removeSizeCallback
+      //
+      // Remove global mesh size callback. For C and C++ only.
+      GMSH_API void removeSizeCallback();
+
       // gmsh::model::mesh::setTransfiniteCurve
       //
       // Set a transfinite meshing constraint on the curve `tag', with `numNodes'
diff --git a/api/gmsh.h_cwrap b/api/gmsh.h_cwrap
index ab793ba594ce31b042bba9c2b6974744cb56d283..ed864d48439484c74c6ac898ee9a577d9920cf51 100644
--- a/api/gmsh.h_cwrap
+++ b/api/gmsh.h_cwrap
@@ -1785,6 +1785,22 @@ namespace gmsh { // Top-level functions
         gmshFree(api_sizes_);
       }
 
+      // Set global mesh size callback. For C and C++ only.
+      inline void setSizeCallback(double (*callback)(int dim, int tag, double x, double y, double z))
+      {
+        int ierr = 0;
+        gmshModelMeshSetSizeCallback(callback, &ierr);
+        if(ierr) throwLastError();
+      }
+
+      // Remove global mesh size callback. For C and C++ only.
+      inline void removeSizeCallback()
+      {
+        int ierr = 0;
+        gmshModelMeshRemoveSizeCallback(&ierr);
+        if(ierr) throwLastError();
+      }
+
       // Set a transfinite meshing constraint on the curve `tag', with `numNodes'
       // nodes distributed according to `meshType' and `coef'. Currently supported
       // types are "Progression" (geometrical progression with power `coef') and
diff --git a/api/gmsh.jl b/api/gmsh.jl
index 47cb6f3488103b4b928611cd17e33f5631b6a660..cebd116ae1e1e7691f3d01d9108df8979e5b8c8f 100644
--- a/api/gmsh.jl
+++ b/api/gmsh.jl
@@ -2197,6 +2197,20 @@ function setSizeAtParametricPoints(dim, tag, parametricCoord, sizes)
     return nothing
 end
 
+"""
+    gmsh.model.mesh.removeSizeCallback()
+
+Remove global mesh size callback. For C and C++ only.
+"""
+function removeSizeCallback()
+    ierr = Ref{Cint}()
+    ccall((:gmshModelMeshRemoveSizeCallback, gmsh.lib), Cvoid,
+          (Ptr{Cint},),
+          ierr)
+    ierr[] != 0 && error(gmsh.logger.getLastError())
+    return nothing
+end
+
 """
     gmsh.model.mesh.setTransfiniteCurve(tag, numNodes, meshType = "Progression", coef = 1.)
 
diff --git a/api/gmsh.py b/api/gmsh.py
index 6f4f3b4723a8985f7666d212799e942411674cf5..daa213274ea62c2583c9de3fb0986d1d1d24e96d 100644
--- a/api/gmsh.py
+++ b/api/gmsh.py
@@ -2602,6 +2602,19 @@ class model:
             if ierr.value != 0:
                 raise Exception(logger.getLastError())
 
+        @staticmethod
+        def removeSizeCallback():
+            """
+            gmsh.model.mesh.removeSizeCallback()
+
+            Remove global mesh size callback. For C and C++ only.
+            """
+            ierr = c_int()
+            lib.gmshModelMeshRemoveSizeCallback(
+                byref(ierr))
+            if ierr.value != 0:
+                raise Exception(logger.getLastError())
+
         @staticmethod
         def setTransfiniteCurve(tag, numNodes, meshType="Progression", coef=1.):
             """
diff --git a/api/gmshc.cpp b/api/gmshc.cpp
index 0b371eea7ae1d6d87229498cb08143d46c527748..f838321516ab7568db42929dc68a7ab7c700fe09 100644
--- a/api/gmshc.cpp
+++ b/api/gmshc.cpp
@@ -1508,6 +1508,28 @@ GMSH_API void gmshModelMeshSetSizeAtParametricPoints(const int dim, const int ta
   }
 }
 
+GMSH_API void gmshModelMeshSetSizeCallback(double (*callback)(int dim, int tag, double x, double y, double z), int * ierr)
+{
+  if(ierr) *ierr = 0;
+  try {
+    gmsh::model::mesh::setSizeCallback(callback);
+  }
+  catch(...){
+    if(ierr) *ierr = 1;
+  }
+}
+
+GMSH_API void gmshModelMeshRemoveSizeCallback(int * ierr)
+{
+  if(ierr) *ierr = 0;
+  try {
+    gmsh::model::mesh::removeSizeCallback();
+  }
+  catch(...){
+    if(ierr) *ierr = 1;
+  }
+}
+
 GMSH_API void gmshModelMeshSetTransfiniteCurve(const int tag, const int numNodes, const char * meshType, const double coef, int * ierr)
 {
   if(ierr) *ierr = 0;
diff --git a/api/gmshc.h b/api/gmshc.h
index 5dd5801bdc662c45b2b2fdec9fc0eb44859f3e01..2513e032cbfe14922bf50928264e0d6826ce5263 100644
--- a/api/gmshc.h
+++ b/api/gmshc.h
@@ -1030,6 +1030,13 @@ GMSH_API void gmshModelMeshSetSizeAtParametricPoints(const int dim,
                                                      double * sizes, size_t sizes_n,
                                                      int * ierr);
 
+/* Set global mesh size callback. For C and C++ only. */
+GMSH_API void gmshModelMeshSetSizeCallback(double (*callback)(int dim, int tag, double x, double y, double z),
+                                           int * ierr);
+
+/* Remove global mesh size callback. For C and C++ only. */
+GMSH_API void gmshModelMeshRemoveSizeCallback(int * ierr);
+
 /* Set a transfinite meshing constraint on the curve `tag', with `numNodes'
  * nodes distributed according to `meshType' and `coef'. Currently supported
  * types are "Progression" (geometrical progression with power `coef') and
diff --git a/doc/texinfo/api.texi b/doc/texinfo/api.texi
index af2f00b7c556956df1755c7448a6a920d1d800e9..019eadb5cd8af381e0e648d852e7128af383bf2c 100644
--- a/doc/texinfo/api.texi
+++ b/doc/texinfo/api.texi
@@ -505,7 +505,7 @@ overall model.
 @item Return:
 integer value
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L77,x2.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.cpp#L11,discrete.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/edges.cpp#L63,edges.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L63,faces.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.cpp#L11,plugin.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L74,x2.py}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.py#L10,discrete.py}, @url{@value{GITLAB-PREFIX}/demos/api/mesh_from_discrete_curve.py#L11,mesh_from_discrete_curve.py}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.py#L9,plugin.py}, @url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L23,reparamOnFace.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L77,x2.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x4.cpp#L24,x4.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.cpp#L11,discrete.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/edges.cpp#L63,edges.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L63,faces.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L74,x2.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L23,x4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L75,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.py#L10,discrete.py}, @url{@value{GITLAB-PREFIX}/demos/api/mesh_from_discrete_curve.py#L11,mesh_from_discrete_curve.py}, ...)
 @end table
 
 @item gmsh/model/removeEntities
@@ -622,7 +622,7 @@ coordinates in @code{coord}, concatenated: [p1x, p1y, p1z, p2x, ...].
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t2.cpp#L91,t2.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t2.py#L87,t2.py}, @url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L21,reparamOnFace.py}, @url{@value{GITLAB-PREFIX}/demos/api/terrain_stl.py#L36,terrain_stl.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t2.cpp#L91,t2.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t2.py#L87,t2.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L73,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L21,reparamOnFace.py}, @url{@value{GITLAB-PREFIX}/demos/api/terrain_stl.py#L36,terrain_stl.py})
 @end table
 
 @item gmsh/model/getDerivative
@@ -661,7 +661,7 @@ u, v parametric coordinates on the surface, concatenated: [p1u, p1v, p2u, ...]).
 @item Return:
 -
 @item Examples:
-Python (@url{@value{GITLAB-PREFIX}/demos/api/normals.py#L28,normals.py})
+Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L38,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L28,normals.py})
 @end table
 
 @item gmsh/model/getPrincipalCurvatures
@@ -693,7 +693,7 @@ triplets of x, y, z components, concatenated: [n1x, n1y, n1z, n2x, ...].
 @item Return:
 -
 @item Examples:
-Python (@url{@value{GITLAB-PREFIX}/demos/api/normals.py#L26,normals.py})
+Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L36,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L26,normals.py})
 @end table
 
 @item gmsh/model/getParametrization
@@ -725,7 +725,7 @@ entity of dimension @code{dim} and tag @code{tag}.
 @item Return:
 -
 @item Examples:
-Python (@url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L18,reparamOnFace.py})
+Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L70,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L18,reparamOnFace.py})
 @end table
 
 @item gmsh/model/isInside
@@ -780,7 +780,7 @@ subset of entities, depending on the underyling geometrical representation.
 @item Return:
 -
 @item Examples:
-Python (@url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L20,reparamOnFace.py})
+Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L72,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L20,reparamOnFace.py})
 @end table
 
 @item gmsh/model/setVisibility
@@ -866,7 +866,7 @@ Set the @code{x}, @code{y}, @code{z} coordinates of a geometrical point.
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L78,x2.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L75,x2.py}, @url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L24,reparamOnFace.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L78,x2.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L75,x2.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L76,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/reparamOnFace.py#L24,reparamOnFace.py})
 @end table
 
 @end ftable
@@ -1043,7 +1043,7 @@ the entity if @code{dim} >= 0 in order to compute their parametric coordinates).
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x1.cpp#L76,x1.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L80,adapt_mesh.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/explore.cpp#L24,explore.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x1.py#L70,x1.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L15,adapt_mesh.py}, @url{@value{GITLAB-PREFIX}/demos/api/explore.py#L20,explore.py}, @url{@value{GITLAB-PREFIX}/demos/api/flatten.py#L26,flatten.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L23,normals.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x1.cpp#L76,x1.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x4.cpp#L67,x4.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L80,adapt_mesh.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/explore.cpp#L24,explore.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x1.py#L70,x1.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L62,x4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L33,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L15,adapt_mesh.py}, @url{@value{GITLAB-PREFIX}/demos/api/explore.py#L20,explore.py}, ...)
 @end table
 
 @item gmsh/model/mesh/getNodesByElementType
@@ -1149,7 +1149,7 @@ automatically assigned to the nodes.
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L91,x2.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.cpp#L14,discrete.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.cpp#L12,plugin.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/view.cpp#L11,view.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L88,x2.py}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.py#L13,discrete.py}, @url{@value{GITLAB-PREFIX}/demos/api/flatten.py#L37,flatten.py}, @url{@value{GITLAB-PREFIX}/demos/api/mesh_from_discrete_curve.py#L16,mesh_from_discrete_curve.py}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.py#L10,plugin.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L91,x2.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x4.cpp#L27,x4.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.cpp#L14,discrete.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.cpp#L12,plugin.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/view.cpp#L11,view.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L88,x2.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L26,x4.py}, @url{@value{GITLAB-PREFIX}/demos/api/discrete.py#L13,discrete.py}, @url{@value{GITLAB-PREFIX}/demos/api/flatten.py#L37,flatten.py}, @url{@value{GITLAB-PREFIX}/demos/api/mesh_from_discrete_curve.py#L16,mesh_from_discrete_curve.py}, ...)
 @end table
 
 @item gmsh/model/mesh/reclassifyNodes
@@ -1403,7 +1403,7 @@ are automatically assigned to the elements.
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L97,x2.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/edges.cpp#L67,edges.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L67,faces.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L94,x2.py}, @url{@value{GITLAB-PREFIX}/demos/api/terrain.py#L59,terrain.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x2.cpp#L97,x2.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x4.cpp#L29,x4.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/edges.cpp#L67,edges.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L67,faces.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x2.py#L94,x2.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L28,x4.py}, @url{@value{GITLAB-PREFIX}/demos/api/terrain.py#L59,terrain.py})
 @end table
 
 @item gmsh/model/mesh/getIntegrationPoints
@@ -1773,6 +1773,30 @@ entities of dimension 1 (lines) are handled.
 -
 @end table
 
+@item gmsh/model/mesh/setSizeCallback
+Set global mesh size callback. For C and C++ only.
+
+@table @asis
+@item Input:
+@code{callback}
+@item Output:
+-
+@item Return:
+-
+@end table
+
+@item gmsh/model/mesh/removeSizeCallback
+Remove global mesh size callback. For C and C++ only.
+
+@table @asis
+@item Input:
+-
+@item Output:
+-
+@item Return:
+-
+@end table
+
 @item gmsh/model/mesh/setTransfiniteCurve
 Set a transfinite meshing constraint on the curve @code{tag}, with
 @code{numNodes} nodes distributed according to @code{meshType} and @code{coef}.
@@ -3322,7 +3346,7 @@ sphere.
 @item Return:
 integer value
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t16.cpp#L53,t16.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t18.cpp#L61,t18.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.cpp#L23,boolean.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L18,faces.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/gui.cpp#L21,gui.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t16.py#L47,t16.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t18.py#L59,t18.py}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.py#L23,boolean.py}, @url{@value{GITLAB-PREFIX}/demos/api/gui.py#L20,gui.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L6,normals.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t16.cpp#L53,t16.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t18.cpp#L61,t18.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.cpp#L23,boolean.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L18,faces.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/gui.cpp#L21,gui.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t16.py#L47,t16.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t18.py#L59,t18.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L16,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.py#L23,boolean.py}, @url{@value{GITLAB-PREFIX}/demos/api/gui.py#L20,gui.py}, ...)
 @end table
 
 @item gmsh/model/occ/addBox
@@ -3339,7 +3363,7 @@ box.
 @item Return:
 integer value
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t16.cpp#L31,t16.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t18.cpp#L27,t18.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.cpp#L22,boolean.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L16,faces.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/gui.cpp#L20,gui.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t16.py#L28,t16.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t18.py#L26,t18.py}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.py#L22,boolean.py}, @url{@value{GITLAB-PREFIX}/demos/api/gui.py#L19,gui.py}, @url{@value{GITLAB-PREFIX}/demos/api/neighbors.py#L11,neighbors.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t16.cpp#L31,t16.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t18.cpp#L27,t18.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x4.cpp#L60,x4.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.cpp#L22,boolean.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/faces.cpp#L16,faces.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t16.py#L28,t16.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t18.py#L26,t18.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L57,x4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L17,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/boolean.py#L22,boolean.py}, ...)
 @end table
 
 @item gmsh/model/occ/addCylinder
@@ -3606,7 +3630,7 @@ set.
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t16.cpp#L61,t16.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t18.cpp#L75,t18.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t20.cpp#L91,t20.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t21.cpp#L44,t21.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/edges.cpp#L23,edges.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t16.py#L54,t16.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t18.py#L70,t18.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t20.py#L73,t20.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t21.py#L34,t21.py}, @url{@value{GITLAB-PREFIX}/demos/api/bspline_bezier_patches.py#L71,bspline_bezier_patches.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t16.cpp#L61,t16.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t18.cpp#L75,t18.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t20.cpp#L91,t20.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t21.cpp#L44,t21.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/edges.cpp#L23,edges.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t16.py#L54,t16.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t18.py#L70,t18.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t20.py#L73,t20.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t21.py#L34,t21.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L60,x5.py}, ...)
 @end table
 
 @item gmsh/model/occ/translate
@@ -3963,7 +3987,7 @@ associate a new tag. Return the view tag.
 @item Return:
 integer value
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t4.cpp#L111,t4.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L246,adapt_mesh.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.cpp#L18,plugin.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/view.cpp#L17,view.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/viewlist.cpp#L20,viewlist.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t4.py#L119,t4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L28,x3.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L20,x4.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L90,adapt_mesh.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L41,normals.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t4.cpp#L111,t4.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x3.cpp#L27,x3.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x4.cpp#L33,x4.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L246,adapt_mesh.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.cpp#L18,plugin.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t4.py#L119,t4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L26,x3.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L32,x4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L51,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L90,adapt_mesh.py}, ...)
 @end table
 
 @item gmsh/view/remove
@@ -4049,6 +4073,8 @@ single vector. For data types that can lead to different data sizes per tag
 -
 @item Return:
 -
+@item Examples:
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x4.cpp#L46,x4.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x4.py#L34,x4.py})
 @end table
 
 @item gmsh/view/getModelData
@@ -4105,7 +4131,7 @@ followed by values per node, repeated for each step: [e1x1, ..., e1xn, e1y1,
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/demos/api/viewlist.cpp#L25,viewlist.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L50,x3.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L42,normals.py}, @url{@value{GITLAB-PREFIX}/demos/api/view_combine.py#L19,view_combine.py}, @url{@value{GITLAB-PREFIX}/demos/api/viewlist.py#L20,viewlist.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x3.cpp#L55,x3.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/viewlist.cpp#L25,viewlist.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L48,x3.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x5.py#L52,x5.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L42,normals.py}, @url{@value{GITLAB-PREFIX}/demos/api/view_combine.py#L19,view_combine.py}, @url{@value{GITLAB-PREFIX}/demos/api/viewlist.py#L20,viewlist.py})
 @end table
 
 @item gmsh/view/getListData
@@ -4146,7 +4172,7 @@ Bold", "Times-Italic", "Times-BoldItalic", "Helvetica", "Helvetica-Bold",
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t4.cpp#L115,t4.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t4.py#L123,t4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L75,x3.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t4.cpp#L115,t4.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/x3.cpp#L81,x3.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t4.py#L123,t4.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L73,x3.py})
 @end table
 
 @item gmsh/view/getListDataStrings
@@ -4186,7 +4212,7 @@ matrices.
 @item Return:
 -
 @item Examples:
-Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L99,x3.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x3.cpp#L110,x3.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L101,x3.py})
 @end table
 
 @item gmsh/view/addAlias
@@ -4267,7 +4293,7 @@ file extension. Append to the file if @code{append} is set.
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L249,adapt_mesh.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.cpp#L44,plugin.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/view.cpp#L25,view.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/viewlist.cpp#L27,viewlist.cpp}), Python (@url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L93,adapt_mesh.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L43,normals.py}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.py#L34,plugin.py}, @url{@value{GITLAB-PREFIX}/demos/api/view.py#L28,view.py}, @url{@value{GITLAB-PREFIX}/demos/api/view_combine.py#L26,view_combine.py}, ...)
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/x3.cpp#L87,x3.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L249,adapt_mesh.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.cpp#L44,plugin.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/view.cpp#L25,view.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/viewlist.cpp#L27,viewlist.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/x3.py#L79,x3.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L93,adapt_mesh.py}, @url{@value{GITLAB-PREFIX}/demos/api/normals.py#L43,normals.py}, @url{@value{GITLAB-PREFIX}/demos/api/plugin.py#L34,plugin.py}, @url{@value{GITLAB-PREFIX}/demos/api/view.py#L28,view.py}, ...)
 @end table
 
 @item gmsh/view/setVisibilityPerWindow
diff --git a/tutorial/c++/t10.cpp b/tutorial/c++/t10.cpp
index e7e51013fad816ba7af93d6f83403b45387c4937..86c1148c2a2998ecb5e84da971b1340464574326 100644
--- a/tutorial/c++/t10.cpp
+++ b/tutorial/c++/t10.cpp
@@ -13,6 +13,12 @@
 #include <gmsh.h>
 #include <sstream>
 
+double meshSizeCallback(int dim, int tag, double x, double y, double z)
+{
+  double val = 0.02 * x + 0.01;
+  return val;
+}
+
 int main(int argc, char **argv)
 {
   gmsh::initialize(argc, argv);
@@ -107,6 +113,10 @@ int main(int argc, char **argv)
 
   gmsh::model::mesh::field::setAsBackgroundMesh(7);
 
+  // In the API we can also set a global mesh size callback, which is called
+  // each time the mesh size is queried
+  gmsh::model::mesh::setSizeCallback(meshSizeCallback);
+
   // To determine the size of mesh elements, Gmsh locally computes the minimum
   // of
   //
@@ -116,7 +126,8 @@ int main(int argc, char **argv)
   // 3) if `Mesh.CharacteristicLengthFromCurvature' is set, the mesh size based
   //    on the curvature and `Mesh.MinimumElementsPerTwoPi';
   // 4) the background mesh field;
-  // 5) any per-entity mesh size constraint.
+  // 5) any per-entity mesh size constraint;
+  // 6) the mesh size returned by the mesh size callback, if any.
   //
   // This value is then constrained in the interval
   // [`Mesh.CharacteristicLengthMin', `MeshCharacteristicLengthMax'] and
diff --git a/tutorial/python/t10.py b/tutorial/python/t10.py
index 6695ad4dfc3a35d6f9df97367c6bb92ebf9dd443..38924eb679d42003dd1f394d2f3dc1dce0b97d2f 100644
--- a/tutorial/python/t10.py
+++ b/tutorial/python/t10.py
@@ -101,6 +101,13 @@ gmsh.model.mesh.field.setNumbers(7, "FieldsList", [2, 3, 5, 6])
 
 gmsh.model.mesh.field.setAsBackgroundMesh(7)
 
+# In the API we can also set a global mesh size callback, which is called each
+# time the mesh size is queried
+def meshSizeCallback(dim, tag, x, y, z):
+    val = 0.02 * x + 0.01
+    return val
+gmsh.model.mesh.setSizeCallback(meshSizeCallback)
+
 # To determine the size of mesh elements, Gmsh locally computes the minimum of
 #
 # 1) the size of the model bounding box;