diff --git a/src/mol-model/structure/model/properties/custom/descriptor.ts b/src/mol-model/structure/model/properties/custom/descriptor.ts
index d86dd94a6fa415db073a466c0493b776854bd575..fd60c265dc356c5f86a44dbf370aa915794b5633 100644
--- a/src/mol-model/structure/model/properties/custom/descriptor.ts
+++ b/src/mol-model/structure/model/properties/custom/descriptor.ts
@@ -6,14 +6,22 @@
 
 import { CifWriter } from 'mol-io/writer/cif'
 import { CifExportContext } from '../../../export/mmcif';
+import { QuerySymbolRuntime } from 'mol-script/runtime/query/compiler';
 
-interface ModelPropertyDescriptor {
+interface ModelPropertyDescriptor<Symbols extends { [name: string]: QuerySymbolRuntime } = { }> {
     readonly isStatic: boolean,
     readonly name: string,
 
     cifExport: {
         categories: CifWriter.Category<CifExportContext>[]
-    }
+    },
+
+    // TODO: add aliases when lisp-like mol-script is done
+    symbols?: Symbols
+}
+
+function ModelPropertyDescriptor<Desc extends ModelPropertyDescriptor>(desc: Desc) {
+    return desc;
 }
 
 export { ModelPropertyDescriptor }
\ No newline at end of file
diff --git a/src/mol-script/language/symbol.ts b/src/mol-script/language/symbol.ts
index de9b536655c7689782e4b84b11edad833aa69fc3..bb935ee6f90b46f622459b68fe982eae660a58e1 100644
--- a/src/mol-script/language/symbol.ts
+++ b/src/mol-script/language/symbol.ts
@@ -74,6 +74,17 @@ export function MSymbol<A extends Arguments, T extends Type>(name: string, args:
     return symbol;
 }
 
+export function CustomPropSymbol<T extends Type>(namespace: string, name: string, type: T, description?: string) {
+    const symbol: MSymbol<Arguments<{}>, T> = function(args: ExpressionArguments<Arguments<{}>['@type']>) {
+        return Expression.Apply(Expression.Symbol(symbol.id), args as any);
+    } as any;
+    symbol.info = { namespace, name, description };
+    symbol.id = `${namespace}.${name}`;
+    symbol.args = Arguments.None;
+    symbol.type = type;
+    return symbol;
+}
+
 export function isSymbol(x: any): x is MSymbol {
     const s = x as MSymbol;
     return typeof s === 'function' && !!s.info && !!s.args && typeof s.info.namespace === 'string' && !!s.type;
diff --git a/src/mol-script/runtime/query/compiler.ts b/src/mol-script/runtime/query/compiler.ts
index 63598bb4c420865970b6e460213f5a5091fa3912..aa3c6b65263172f15b6c2f37321788f4f1b146f0 100644
--- a/src/mol-script/runtime/query/compiler.ts
+++ b/src/mol-script/runtime/query/compiler.ts
@@ -5,7 +5,7 @@
  */
 
 import Expression from '../../language/expression';
-import { QueryContext, QueryFn, Structure } from 'mol-model/structure';
+import { QueryContext, QueryFn, Structure, ModelPropertyDescriptor } from 'mol-model/structure';
 import { MSymbol } from '../../language/symbol';
 
 export class QueryRuntimeTable {
@@ -15,6 +15,14 @@ export class QueryRuntimeTable {
         this.map.set(runtime.symbol.id, runtime);
     }
 
+    addCustomProp(desc: ModelPropertyDescriptor) {
+        if (!desc.symbols) return;
+
+        for (const k of Object.keys(desc.symbols)) {
+            this.addSymbol((desc.symbols as any)[k]);
+        }
+    }
+
     getRuntime(id: string) {
         return this.map.get(id);
     }
diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts
index d5c4217d74a0c2dd107a7b509a8b9f3a4b320f0a..e0d555655a2369e68a413f601558c95047766169 100644
--- a/src/mol-script/runtime/query/table.ts
+++ b/src/mol-script/runtime/query/table.ts
@@ -30,6 +30,10 @@ const symbols = [
     C(MolScript.core.rel.lte, (ctx, v) => v[0](ctx) <= v[1](ctx)),
     C(MolScript.core.rel.gr, (ctx, v) => v[0](ctx) > v[1](ctx)),
     C(MolScript.core.rel.gre, (ctx, v) => v[0](ctx) >= v[1](ctx)),
+    C(MolScript.core.rel.inRange, (ctx, v) => {
+        const x = v[0](ctx);
+        return x >= v[1](ctx) && x <= v[2](ctx);
+    }),
 
     // ============= TYPES ================
     C(MolScript.structureQuery.type.elementSymbol, (ctx, v) => ElementSymbol(v[0](ctx))),
@@ -46,16 +50,6 @@ const symbols = [
     // ============= ATOM PROPERTIES ================
     D(MolScript.structureQuery.atomProperty.macromolecular.label_comp_id, (ctx, _) => StructureProperties.residue.label_comp_id(ctx.element)),
     D(MolScript.structureQuery.atomProperty.core.elementSymbol, (ctx, _) => StructureProperties.atom.type_symbol(ctx.element))
-
-    // Symbol(MolQL.core.rel.neq, staticAttr)((env, v) => v[0](env) !== v[1](env)),
-    // Symbol(MolQL.core.rel.lt, staticAttr)((env, v) => v[0](env) < v[1](env)),
-    // Symbol(MolQL.core.rel.lte, staticAttr)((env, v) => v[0](env) <= v[1](env)),
-    // Symbol(MolQL.core.rel.gr, staticAttr)((env, v) => v[0](env) > v[1](env)),
-    // Symbol(MolQL.core.rel.gre, staticAttr)((env, v) => v[0](env) >= v[1](env)),
-    // Symbol(MolQL.core.rel.inRange, staticAttr)((env, v) => {
-    //     const x = v[0](env);
-    //     return x >= v[1](env) && x <= v[2](env)
-    // }),
 ];
 
 (function () {
diff --git a/src/perf-tests/mol-script.ts b/src/perf-tests/mol-script.ts
index 104c6c1949383142a133d76c5a259a6c71dcfd01..5f6cfcc9ead8c6322b2a1a6bb21e113c3cd4edb1 100644
--- a/src/perf-tests/mol-script.ts
+++ b/src/perf-tests/mol-script.ts
@@ -1,7 +1,9 @@
 import { MolScriptBuilder } from 'mol-script/language/builder';
-import { compile } from 'mol-script/runtime/query/compiler';
-import { QueryContext, Structure, StructureQuery } from 'mol-model/structure';
+import { compile, QuerySymbolRuntime, DefaultQueryRuntimeTable } from 'mol-script/runtime/query/compiler';
+import { QueryContext, Structure, StructureQuery, ModelPropertyDescriptor } from 'mol-model/structure';
 import { readCifFile, getModelsAndStructure } from '../apps/structure-info/model';
+import { CustomPropSymbol } from 'mol-script/language/symbol';
+import Type from 'mol-script/language/type';
 
 // import Examples from 'mol-script/script/mol-script/examples'
 // import { parseMolScript } from 'mol-script/script/mol-script/parser'
@@ -27,6 +29,21 @@ const compiled = compile<number>(expr);
 const result = compiled(new QueryContext(Structure.Empty));
 console.log(result);
 
+const CustomProp = ModelPropertyDescriptor({
+    name: 'test_prop',
+    isStatic: true,
+    cifExport: { categories: [ ]},
+    symbols: {
+        residueIndex: QuerySymbolRuntime.Dynamic(CustomPropSymbol('custom.test-prop', 'residue-index', Type.Num), ctx => {
+            const e = ctx.element;
+            //console.log(e.element, e.unit.model.atomicHierarchy.residueAtomSegments.index[e.element])
+            return e.unit.model.atomicHierarchy.residueAtomSegments.index[e.element];
+        })
+    }
+});
+
+DefaultQueryRuntimeTable.addCustomProp(CustomProp);
+
 async function testQ() {
     const frame = await readCifFile('e:/test/quick/1cbs_updated.cif');
     const { structure } = await getModelsAndStructure(frame);
@@ -36,10 +53,11 @@ async function testQ() {
             MolScriptBuilder.struct.atomProperty.core.elementSymbol(),
             MolScriptBuilder.es('C')
         ]),
-        'residue-test': MolScriptBuilder.core.rel.eq([
-            MolScriptBuilder.struct.atomProperty.macromolecular.label_comp_id(),
-            'REA'
-        ])
+        // 'residue-test': MolScriptBuilder.core.rel.eq([
+        //     MolScriptBuilder.struct.atomProperty.macromolecular.label_comp_id(),
+        //     'REA'
+        // ])
+        'residue-test': MolScriptBuilder.core.rel.inRange([CustomProp.symbols.residueIndex.symbol(), 1, 5])
     });
 
     const compiled = compile<StructureQuery>(expr);