diff --git a/docs/release-notes-editor.txt b/docs/release-notes-editor.txt
index 6a8d41bc9fb18f2987e7a24cf2a34f71d104c24e..0226e5131caaca30a2d0ec3fa0077094b9062e90 100644
--- a/docs/release-notes-editor.txt
+++ b/docs/release-notes-editor.txt
@@ -13,6 +13,9 @@ Editor
 - Ermöglicht die Darstellung eines optionalen Tastatursymbols bei Eingabefeldern und -bereichen
 - Erlaubt Validierungen ohne Warnmeldungen
   Warnmeldungen können nun ohne Zeichenketten abgespeichert werden.
+- Ermöglicht die Definition eigener Zeichen für die Eingabehilfe
+  Durch Auswahl der Option "Eigene Zeichen" öffnet sich ein Textfeld,
+  in das die gewünschten Zeichen als ein Wort eingegeben werden.
 
 1.34.0
 - Implement GeoGebra applet element
diff --git a/docs/release-notes-player.txt b/docs/release-notes-player.txt
index e83b58e06e2af03afe771f9c23c72f1e8639b099..2f2819f6c54a3ffea5ced4dfe8a0888ff8720dfe 100644
--- a/docs/release-notes-player.txt
+++ b/docs/release-notes-player.txt
@@ -10,6 +10,8 @@ Player
   werden nach erster Benutzung der Elemente ausgeblendet
 - Verbessert die Performanz von GeoGebra-Elementen
 - Zeigt Validierungsfehler ohne angegebene Warnmeldungen nur mit rotem Rahmen an
+- Die Eingabehilfe "Eigene Zeichen" nutzt weitestgehend das Layout
+  der Eingabehilfe "Französische Sonderzeichen"
 
 1.27.0
 - Add new input assistance presets ('Space' and 'Comma').
diff --git a/docs/unit_definition_changelog.txt b/docs/unit_definition_changelog.txt
index 17f3a3777f3be9d733530f345ed58b1968a78735..c7320369c99cf85f82983feee558e47a54fa486e 100644
--- a/docs/unit_definition_changelog.txt
+++ b/docs/unit_definition_changelog.txt
@@ -52,3 +52,5 @@ iqb-aspect-definition@1.0.0
                     +expectedCharactersCount: number;
                     +hasKeyboardIcon: boolean;
 - TextFieldElement: +hasKeyboardIcon: boolean;
+- TextFieldElement/TextFieldSimpleElement/SpellCorrectElement/TextAreaElement: InputAssistancePreset +'custom'
+                                                                               +inputAssistanceCustomKeys: string;
diff --git a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts
index a24b51a7f101c961bb1935be4e6d898a2b778a0d..7d5ff006917452646e320931ccd5d17cf1488f14 100644
--- a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts
+++ b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts
@@ -15,6 +15,7 @@ export class TextFieldSimpleElement extends InputElement {
   pattern: string | null = null;
   patternWarnMessage: string = 'Eingabe entspricht nicht der Vorgabe';
   inputAssistancePreset: InputAssistancePreset = null;
+  inputAssistanceCustomKeys: string = '';
   inputAssistancePosition: 'floating' | 'right' = 'floating';
   restrictedToInputAssistanceChars: boolean = true;
   hasArrowKeys: boolean = false;
@@ -34,6 +35,9 @@ export class TextFieldSimpleElement extends InputElement {
     if (element.pattern) this.pattern = element.pattern;
     if (element.patternWarnMessage !== undefined) this.patternWarnMessage = element.patternWarnMessage;
     if (element.inputAssistancePreset) this.inputAssistancePreset = element.inputAssistancePreset;
+    if (element.inputAssistanceCustomKeys !== undefined) {
+      this.inputAssistanceCustomKeys = element.inputAssistanceCustomKeys;
+    }
     if (element.inputAssistancePosition) this.inputAssistancePosition = element.inputAssistancePosition;
     if (element.restrictedToInputAssistanceChars !== undefined) {
       this.restrictedToInputAssistanceChars = element.restrictedToInputAssistanceChars;
diff --git a/projects/common/models/elements/element.ts b/projects/common/models/elements/element.ts
index d883aca9b32c924be7bf4e99f65f55b71261ed73..35f1959522ff1be7a1baccf02504ec6712a2f794 100644
--- a/projects/common/models/elements/element.ts
+++ b/projects/common/models/elements/element.ts
@@ -13,7 +13,7 @@ TextLabel | TextLabel[] | ClozeDocument | LikertRowElement[] | Hotspot[] |
 PositionProperties | PlayerProperties | BasicStyles;
 
 export type InputAssistancePreset = null | 'french' | 'numbers' | 'numbersAndOperators' | 'numbersAndBasicOperators'
-| 'comparisonOperators' | 'squareDashDot' | 'placeValue' | 'space' | 'comma';
+| 'comparisonOperators' | 'squareDashDot' | 'placeValue' | 'space' | 'comma' | 'custom';
 
 export abstract class UIElement {
   [index: string]: unknown;
diff --git a/projects/common/models/elements/input-elements/spell-correct.ts b/projects/common/models/elements/input-elements/spell-correct.ts
index 9f687bb99bd5f201b247d4d5eb0d3da01ef0f556..e635cb99552b002d1b97706598acb955e351c7ba 100644
--- a/projects/common/models/elements/input-elements/spell-correct.ts
+++ b/projects/common/models/elements/input-elements/spell-correct.ts
@@ -11,6 +11,7 @@ import { SpellCorrectComponent } from 'common/components/input-elements/spell-co
 
 export class SpellCorrectElement extends InputElement implements PositionedUIElement {
   inputAssistancePreset: InputAssistancePreset = null;
+  inputAssistanceCustomKeys: string = '';
   inputAssistancePosition: 'floating' | 'right' = 'floating';
   restrictedToInputAssistanceChars: boolean = true;
   hasArrowKeys: boolean = false;
@@ -22,6 +23,9 @@ export class SpellCorrectElement extends InputElement implements PositionedUIEle
   constructor(element: Partial<SpellCorrectElement>) {
     super({ width: 230, height: 80, ...element });
     if (element.inputAssistancePreset) this.inputAssistancePreset = element.inputAssistancePreset;
+    if (element.inputAssistanceCustomKeys !== undefined) {
+      this.inputAssistanceCustomKeys = element.inputAssistanceCustomKeys;
+    }
     if (element.inputAssistancePosition) this.inputAssistancePosition = element.inputAssistancePosition;
     if (element.restrictedToInputAssistanceChars !== undefined) {
       this.restrictedToInputAssistanceChars = element.restrictedToInputAssistanceChars;
diff --git a/projects/common/models/elements/input-elements/text-area.ts b/projects/common/models/elements/input-elements/text-area.ts
index c29c0f3826bb2282a1bebcde46dbcd2cd6a42bb0..dc74f4198d35d1d5a7a2eb74a8921df6986c5a25 100644
--- a/projects/common/models/elements/input-elements/text-area.ts
+++ b/projects/common/models/elements/input-elements/text-area.ts
@@ -16,6 +16,7 @@ export class TextAreaElement extends InputElement implements PositionedUIElement
   rowCount: number = 3;
   expectedCharactersCount: number = 300;
   inputAssistancePreset: InputAssistancePreset = null;
+  inputAssistanceCustomKeys: string = '';
   inputAssistancePosition: 'floating' | 'right' = 'floating';
   hasArrowKeys: boolean = false;
   hasReturnKey: boolean = false;
@@ -36,6 +37,9 @@ export class TextAreaElement extends InputElement implements PositionedUIElement
     if (element.hasDynamicRowCount) this.hasDynamicRowCount = element.hasDynamicRowCount;
     if (element.expectedCharactersCount) this.expectedCharactersCount = element.expectedCharactersCount;
     if (element.inputAssistancePreset) this.inputAssistancePreset = element.inputAssistancePreset;
+    if (element.inputAssistanceCustomKeys !== undefined) {
+      this.inputAssistanceCustomKeys = element.inputAssistanceCustomKeys;
+    }
     if (element.inputAssistancePosition) this.inputAssistancePosition = element.inputAssistancePosition;
     if (element.restrictedToInputAssistanceChars !== undefined) {
       this.restrictedToInputAssistanceChars = element.restrictedToInputAssistanceChars;
diff --git a/projects/common/models/elements/input-elements/text-field.ts b/projects/common/models/elements/input-elements/text-field.ts
index c6e1750c0ad147fc210e6e911254533eb4bed103..e9ed44c084a4547915d98ce25a0bb4d5f6fe4fff 100644
--- a/projects/common/models/elements/input-elements/text-field.ts
+++ b/projects/common/models/elements/input-elements/text-field.ts
@@ -15,6 +15,7 @@ export class TextFieldElement extends InputElement implements PositionedUIElemen
   pattern: string | null = null;
   patternWarnMessage: string = 'Eingabe entspricht nicht der Vorgabe';
   inputAssistancePreset: InputAssistancePreset = null;
+  inputAssistanceCustomKeys: string = '';
   inputAssistancePosition: 'floating' | 'right' = 'floating';
   restrictedToInputAssistanceChars: boolean = true;
   hasArrowKeys: boolean = false;
@@ -37,6 +38,9 @@ export class TextFieldElement extends InputElement implements PositionedUIElemen
     if (element.pattern !== undefined) this.pattern = element.pattern;
     if (element.patternWarnMessage !== undefined) this.patternWarnMessage = element.patternWarnMessage;
     if (element.inputAssistancePreset) this.inputAssistancePreset = element.inputAssistancePreset;
+    if (element.inputAssistanceCustomKeys !== undefined) {
+      this.inputAssistanceCustomKeys = element.inputAssistanceCustomKeys;
+    }
     if (element.inputAssistancePosition) this.inputAssistancePosition = element.inputAssistancePosition;
     if (element.restrictedToInputAssistanceChars !== undefined) {
       this.restrictedToInputAssistanceChars = element.restrictedToInputAssistanceChars;
diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component.ts
index 28cdb0a99b28116ae2c1789467f617ac0e6aee5c..018a2616332f5272aa45834adb2bba8272200566 100644
--- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component.ts
+++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component.ts
@@ -102,12 +102,21 @@ import { CombinedProperties } from 'editor/src/app/components/properties-panel/e
       <mat-select [value]="combinedProperties.inputAssistancePreset"
                   (selectionChange)="updateModel.emit({ property: 'inputAssistancePreset', value: $event.value })">
         <mat-option *ngFor="let option of [null, 'french', 'numbers', 'numbersAndOperators', 'numbersAndBasicOperators',
-       'comparisonOperators', 'squareDashDot', 'placeValue', 'space', 'comma']"
+       'comparisonOperators', 'squareDashDot', 'placeValue', 'space', 'comma', 'custom']"
                     [value]="option">
           {{ option === null ? ('propertiesPanel.none' | translate) : ('propertiesPanel.' + option | translate) }}
         </mat-option>
       </mat-select>
     </mat-form-field>
+
+    <mat-form-field *ngIf="combinedProperties.inputAssistancePreset === 'custom'"
+                    class="wide-form-field" appearance="fill">
+      <mat-label>{{'propertiesPanel.inputAssistanceCustomKeys' | translate }}</mat-label>
+      <input matInput type="text"
+             [value]="$any(combinedProperties.inputAssistanceCustomKeys)"
+             (input)="updateModel.emit({ property: 'inputAssistanceCustomKeys', value: $any($event.target).value })">
+    </mat-form-field>
+
     <mat-form-field *ngIf="combinedProperties.inputAssistancePreset !== null &&
                            combinedProperties.inputAssistancePosition !== undefined"
                     appearance="fill">
diff --git a/projects/editor/src/assets/i18n/de.json b/projects/editor/src/assets/i18n/de.json
index bd37edc0cac3e00edcbdac946a27ef0963a5ef83..387202ddf1298480438dd64787bfb3823b857018 100644
--- a/projects/editor/src/assets/i18n/de.json
+++ b/projects/editor/src/assets/i18n/de.json
@@ -133,6 +133,8 @@
     "squareDashDot": "Quadrat Strich Punkt",
     "placeValue": "Stellenwert",
     "comparisonOperators": "Vergleichsoperatoren",
+    "custom": "Eigene Zeichen",
+    "inputAssistanceCustomKeys": "Zeichenkette",
     "inputAssistancePosition": "Eingabehilfeposition",
     "floating": "schwebend",
     "showSoftwareKeyboard": "Tastatur einblenden",
diff --git a/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.css b/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.css
index ae36ce96808fa2a683f40f959df72114b235eda8..9953287e4888ace49df40e91ec935f0656081cc9 100644
--- a/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.css
+++ b/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.css
@@ -3,6 +3,10 @@
   margin: auto;
 }
 
+.floating-row-container {
+  max-width: 354px;
+}
+
 .grid-layout {
   justify-content: center;
   margin-top: 15px;
diff --git a/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.html b/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.html
index ac09adb3659d3fe99ffc65d25bcab5a9d0011e68..3698e6a63920d3b256624e9f4a18f959c98e8459 100644
--- a/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.html
+++ b/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.html
@@ -1,4 +1,5 @@
-<div [class.fixed-row-container]="position === 'right' && preset === 'french'">
+<div [class.fixed-row-container]="position === 'right' && ( preset === 'french' || preset === 'custom' )"
+     [class.floating-row-container]="position === 'floating'">
   <div *ngFor="let row of rows; let last = last">
     <ng-container *ngFor="let key of row; let index = index; let lastRowKey = last">
       <aspect-keypad-key
diff --git a/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.ts b/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.ts
index c9e9f569c8a941f42b1602e189829904926a6b0e..87f3fb53ba12676520636c24fb8a2c660c4067a1 100644
--- a/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.ts
+++ b/projects/player/modules/key-input/components/keypad-layout/keypad-layout.component.ts
@@ -3,7 +3,7 @@ import {
 } from '@angular/core';
 import { InputAssistancePreset } from 'common/models/elements/element';
 import { KeyInputRestrictionDirective } from '../../directives/key-input-restriction.directive';
-import { KeyLayout } from '../../configs/key-layout';
+import { KeyInputLayout } from '../../configs/key-layout';
 
 @Component({
   selector: 'aspect-keypad-layout',
@@ -12,6 +12,7 @@ import { KeyLayout } from '../../configs/key-layout';
 })
 export class KeypadLayoutComponent extends KeyInputRestrictionDirective implements OnInit {
   @Input() preset!: InputAssistancePreset;
+  @Input() layout!: KeyInputLayout;
   @Input() position!: 'floating' | 'right';
 
   @Output() keyClicked = new EventEmitter<string>();
@@ -21,8 +22,8 @@ export class KeypadLayoutComponent extends KeyInputRestrictionDirective implemen
   shift: boolean = false;
 
   ngOnInit(): void {
-    this.rows = KeyLayout.get(this.preset).default;
-    this.additionalRows = KeyLayout.get(this.preset).additional;
+    this.rows = this.layout.default;
+    this.additionalRows = this.layout.additional;
     this.allowedKeys = [
       ...this.rows.flat().filter(key => key.length === 1),
       ...this.additionalRows.flat().filter(key => key.length === 1)
@@ -45,6 +46,6 @@ export class KeypadLayoutComponent extends KeyInputRestrictionDirective implemen
 
   toggleShift(): void {
     this.shift = !this.shift;
-    this.rows = this.shift ? KeyLayout.get(this.preset).shift : KeyLayout.get(this.preset).default;
+    this.rows = this.shift ? this.layout.shift : this.layout.default;
   }
 }
diff --git a/projects/player/modules/key-input/components/keypad/keypad.component.html b/projects/player/modules/key-input/components/keypad/keypad.component.html
index c8bb4b239a78f9156a332fe5159eaadb9044ea6f..17febc138b5e4deb91089d292161bd3ffc40236d 100644
--- a/projects/player/modules/key-input/components/keypad/keypad.component.html
+++ b/projects/player/modules/key-input/components/keypad/keypad.component.html
@@ -10,6 +10,7 @@
     <aspect-keypad-layout
         [inputElement]="inputElement"
         [preset]="preset"
+        [layout]="layout"
         [position]="position"
         [restrictToAllowedKeys]="restrictToAllowedKeys"
         [hasArrowKeys]="hasArrowKeys"
diff --git a/projects/player/modules/key-input/components/keypad/keypad.component.ts b/projects/player/modules/key-input/components/keypad/keypad.component.ts
index c9d62b232b5c7f024a07fcf0148729bc581293ad..09b7677efeffb48c27e425b9cc80d1055c10eb8a 100644
--- a/projects/player/modules/key-input/components/keypad/keypad.component.ts
+++ b/projects/player/modules/key-input/components/keypad/keypad.component.ts
@@ -1,15 +1,17 @@
 import {
-  Component, EventEmitter, Input, Output
+  Component, EventEmitter, Input, OnInit, Output
 } from '@angular/core';
 import { InputAssistancePreset } from 'common/models/elements/element';
+import { KeyInputLayout, KeyLayout } from 'player/modules/key-input/configs/key-layout';
 
 @Component({
   selector: 'aspect-keypad',
   templateUrl: './keypad.component.html',
   styleUrls: ['./keypad.component.css']
 })
-export class KeypadComponent {
+export class KeypadComponent implements OnInit {
   @Input() preset!: InputAssistancePreset;
+  @Input() customKeys!: string;
   @Input() position!: 'floating' | 'right';
   @Input() inputElement!: HTMLTextAreaElement | HTMLInputElement;
   @Input() positionOffset!: number;
@@ -21,6 +23,11 @@ export class KeypadComponent {
   @Output() keyClicked = new EventEmitter<string>();
 
   arrows: string[] = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
+  layout: KeyInputLayout = { default: [], shift: [], additional: [] };
+
+  ngOnInit(): void {
+    this.layout = KeyLayout.get(this.preset, this.customKeys);
+  }
 
   evaluateClickedKeyValue(key: string): void {
     if (this.arrows.includes(key)) {
diff --git a/projects/player/modules/key-input/configs/key-layout.ts b/projects/player/modules/key-input/configs/key-layout.ts
index b26e660b9efb73c7b25640c8cfadfce4b71acec5..06430e0fb43b6569d970820c1f28a6319d6cdda4 100644
--- a/projects/player/modules/key-input/configs/key-layout.ts
+++ b/projects/player/modules/key-input/configs/key-layout.ts
@@ -1,11 +1,13 @@
 import { InputAssistancePreset } from 'common/models/elements/element';
 
+export interface KeyInputLayout {
+  default: string[][],
+  shift: string[][],
+  additional: string[][]
+}
+
 export class KeyLayout {
-  static get = (preset: InputAssistancePreset | 'keyboard'): {
-    default: string[][],
-    shift: string[][],
-    additional: string[][]
-  } => {
+  static get = (preset: InputAssistancePreset | 'keyboard', customKeys: string = ''): KeyInputLayout => {
     switch (preset) {
       case 'french': {
         return {
@@ -112,7 +114,7 @@ export class KeyLayout {
           additional: [[]]
         };
       }
-      default: { // keyboard
+      case 'keyboard': {
         return {
           default: [
             ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'ß', 'Backspace'],
@@ -131,6 +133,15 @@ export class KeyLayout {
           additional: [[]]
         };
       }
+      default: { // custom
+        return {
+          default: [
+            customKeys.split('')
+          ],
+          shift: [[]],
+          additional: [[]]
+        };
+      }
     }
   };
 }
diff --git a/projects/player/src/app/components/floating-keypad/floating-keypad.component.html b/projects/player/src/app/components/floating-keypad/floating-keypad.component.html
index a41123ccd32dbefdca522fab645f4433942840f0..842e27cfa39a86dd3066979d8a2800ce634ae286 100644
--- a/projects/player/src/app/components/floating-keypad/floating-keypad.component.html
+++ b/projects/player/src/app/components/floating-keypad/floating-keypad.component.html
@@ -9,6 +9,7 @@
       [inputElement]="keypadService.inputElement"
       [position]="keypadService.position"
       [preset]="keypadService.preset"
+      [customKeys]="keypadService.elementComponent.elementModel.inputAssistanceCustomKeys"
       [restrictToAllowedKeys]="keypadService.elementComponent.elementModel.restrictedToInputAssistanceChars"
       [hasArrowKeys]="keypadService.elementComponent.elementModel.hasArrowKeys"
       [hasReturnKey]="!!keypadService.elementComponent.elementModel.hasReturnKey"
diff --git a/projects/player/src/app/components/layouts/player-layout/player-layout.component.html b/projects/player/src/app/components/layouts/player-layout/player-layout.component.html
index 3ec577af701f4713c99bf91736685f661459c6bf..8473b75daf41315bb3581762f7d70fc7b706d975 100644
--- a/projects/player/src/app/components/layouts/player-layout/player-layout.component.html
+++ b/projects/player/src/app/components/layouts/player-layout/player-layout.component.html
@@ -12,6 +12,7 @@
         [inputElement]="keypadService.inputElement"
         [position]="keypadService.position"
         [preset]="keypadService.preset"
+        [customKeys]="keypadService.elementComponent.elementModel.inputAssistanceCustomKeys"
         [restrictToAllowedKeys]="keypadService.elementComponent.elementModel.restrictedToInputAssistanceChars"
         [hasArrowKeys]="keypadService.elementComponent.elementModel.hasArrowKeys"
         [hasReturnKey]="!!keypadService.elementComponent.elementModel.hasReturnKey"
diff --git a/projects/player/src/app/services/keypad.service.ts b/projects/player/src/app/services/keypad.service.ts
index af0af2b98e7b93928ae94399fda7de033c05bf2c..f9f529e4c922931ceb1c800f44e21fa8f2450299 100644
--- a/projects/player/src/app/services/keypad.service.ts
+++ b/projects/player/src/app/services/keypad.service.ts
@@ -1,12 +1,12 @@
 import { Injectable } from '@angular/core';
 import { TextFieldComponent } from 'common/components/input-elements/text-field.component';
 import { TextAreaComponent } from 'common/components/input-elements/text-area.component';
-import { InputService } from '../classes/input-service';
 import { SpellCorrectComponent } from 'common/components/input-elements/spell-correct.component';
 import {
   TextFieldSimpleComponent
 } from 'common/components/compound-elements/cloze/cloze-child-elements/text-field-simple.component';
 import { InputAssistancePreset } from 'common/models/elements/element';
+import { InputService } from '../classes/input-service';
 
 @Injectable({
   providedIn: 'root'